From 4a0ea05ec901d34e871d0a9c39a6f6df87e3d39c Mon Sep 17 00:00:00 2001 From: Brian Date: Sat, 8 Jan 2022 23:04:18 -0500 Subject: [PATCH 01/20] wip --- .env.prod | 7 +- .env.qa | 8 - CHANGELOG.md | 4 + package.json | 6 +- src/app/events/VoiceStateUpdate.ts | 4 +- src/app/events/poap/AddUserForEvent.ts | 128 --------- .../poap/HandleParticipantDuringEvent.ts | 263 ++++++++++++++++++ src/app/service/poap/start/StartPOAP.ts | 2 +- 8 files changed, 274 insertions(+), 148 deletions(-) delete mode 100644 src/app/events/poap/AddUserForEvent.ts create mode 100644 src/app/events/poap/HandleParticipantDuringEvent.ts diff --git a/.env.prod b/.env.prod index fcfd752c..6aed6a39 100644 --- a/.env.prod +++ b/.env.prod @@ -22,18 +22,13 @@ DISCORD_CHANNEL_DEV_WORKROOM_ID=841349002330505266 DISCORD_CHANNEL_WRITERS_ROOM_ID=841332222946312232 # Logger -LOGDNA_APP_NAME=degen-tbd +LOGDNA_APP_NAME=degen LOGDNA_DEFAULT_LEVEL=info # POAP POAP_REQUIRED_PARTICIPATION_DURATION=10 POAP_MAX_EVENT_DURATION_MINUTES=180 -# MISC -DAO_CURRENT_SEASON=2 -DAO_CURRENT_SEASON_END_DATE=2022-01-07T04:00:00.000Z -DAO_GUEST_PASS_EXPIRATION_DAYS=14 - # Twitter TWITTER_API_TOKEN= TWITTER_API_SECRET= diff --git a/.env.qa b/.env.qa index a78ccf28..9deb5eb4 100644 --- a/.env.qa +++ b/.env.qa @@ -46,18 +46,10 @@ DISCORD_CHANNEL_FIRST_QUEST_PROJECT_ID=854401837566001192 LOGDNA_APP_NAME=serendipity-mk1 LOGDNA_DEFAULT_LEVEL=debug -# MISC -DAO_CURRENT_SEASON=2 -DAO_CURRENT_SEASON_END_DATE=2022-01-07T04:00:00.000Z -DAO_GUEST_PASS_EXPIRATION_DAYS=14 - # POAP POAP_REQUIRED_PARTICIPATION_DURATION=10 POAP_MAX_EVENT_DURATION_MINUTES=180 -# URLs -DAO_BOUNTY_BOARD_URL=https://develop--bounty-board-29081e.netlify.app/ - # Twitter TWITTER_API_TOKEN= TWITTER_API_SECRET= diff --git a/CHANGELOG.md b/CHANGELOG.md index adb3a1f0..85923756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 2.6.0-SNAPSHOT + +1. Stability check + ## 2.5.2-RELEASE 1. Address sentry.io issues 2022-01-08 diff --git a/package.json b/package.json index d2ab4596..a1168054 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "degen-tbd", - "version": "2.5.2", + "name": "degen", + "version": "2.6.0", "description": "Administrative and Utilitarian bot for the Bankless Discord Server.", "main": "app.js", "private": true, @@ -9,7 +9,7 @@ "test": "jest", "qa": "node -r dotenv/config --trace-warnings dist/app/app.js dotenv_config_path=.env.qa", "prod": "node -r dotenv/config --trace-warnings dist/app/app.js dotenv_config_path=.env.prod", - "prestart": "yarn install && yarn build", + "prestart": "yarn install && yarn build && yarn lint", "pretest": "yarn install && yarn build", "start": "node --trace-warnings -r dotenv/config dist/app/app.js", "lint": "eslint . --ext .ts", diff --git a/src/app/events/VoiceStateUpdate.ts b/src/app/events/VoiceStateUpdate.ts index 2bae8342..b5445576 100644 --- a/src/app/events/VoiceStateUpdate.ts +++ b/src/app/events/VoiceStateUpdate.ts @@ -1,7 +1,7 @@ import { VoiceState } from 'discord.js'; -import addUserForEvent from './poap/AddUserForEvent'; import { DiscordEvent } from '../types/discord/DiscordEvent'; import { LogUtils } from '../utils/Log'; +import HandleParticipantDuringEvent from './poap/HandleParticipantDuringEvent'; /** * voiceStateUpdate @@ -18,7 +18,7 @@ export default class implements DiscordEvent { */ async execute(oldState: VoiceState, newState: VoiceState): Promise { try { - await addUserForEvent(oldState, newState).catch(e => LogUtils.logError('failed to add user for POAP event', e, oldState.guild.id)); + await HandleParticipantDuringEvent(oldState, newState).catch(e => LogUtils.logError('failed to add user for POAP event', e, oldState.guild.id)); } catch (e) { LogUtils.logError('failed to process event voiceStateUpdate', e); } diff --git a/src/app/events/poap/AddUserForEvent.ts b/src/app/events/poap/AddUserForEvent.ts deleted file mode 100644 index b9bac62a..00000000 --- a/src/app/events/poap/AddUserForEvent.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { Guild, GuildChannel, GuildMember, VoiceState } from 'discord.js'; -import { Collection, Cursor, Db, InsertOneWriteOpResult, MongoError } from 'mongodb'; -import constants from '../../service/constants/constants'; -import { POAPSettings } from '../../types/poap/POAPSettings'; -import { POAPParticipant } from '../../types/poap/POAPParticipant'; -import Log, { LogUtils } from '../../utils/Log'; -import dayjs, { Dayjs } from 'dayjs'; -import EndPOAP from '../../service/poap/end/EndPOAP'; -import MongoDbUtils from '../../utils/MongoDbUtils'; - -export default async (oldState: VoiceState, newState: VoiceState): Promise => { - if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { - // user did not change channels - return; - } - - const guild: Guild = (oldState.guild != null) ? oldState.guild : newState.guild; - const member: GuildMember | null = (oldState.guild != null) ? oldState.member : newState.member; - - if (member == null) { - // could not find member - return; - } - - const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); - db.collection(constants.DB_COLLECTION_POAP_SETTINGS); - - const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); - const activeChannelsCursor: Cursor = await poapSettingsDB.find({ - isActive: true, - discordServerId: `${guild.id}`, - }); - for await (const poapSetting of activeChannelsCursor) { - const currentDate: Dayjs = dayjs(); - try { - const endDate: Dayjs = (poapSetting.endTime == null) ? currentDate : dayjs(poapSetting.endTime); - if (currentDate.isBefore(endDate)) { - const voiceChannel: GuildChannel | null = await guild.channels.fetch(poapSetting.voiceChannelId); - if (voiceChannel == null) { - Log.warn('voice channel might have been deleted.'); - return; - } - await addUserToDb(oldState, newState, db, voiceChannel, member); - } else { - Log.debug(`current date is after or equal to event end date, currentDate: ${currentDate}, endDate: ${endDate}`); - const poapOrganizerGuildMember: GuildMember = await guild.members.fetch(poapSetting.discordUserId); - await EndPOAP(poapOrganizerGuildMember, constants.PLATFORM_TYPE_DISCORD); - } - } catch (e) { - LogUtils.logError(`failed to add ${member.user.tag} to db`, e); - } - } -}; - -export const addUserToDb = async ( - oldState: VoiceState, newState: VoiceState, db: Db, channel: GuildChannel, member: GuildMember, -): Promise => { - if (!(newState.channelId === channel.id || oldState.channelId === channel.id)) { - // event change is not related to event parameter - return; - } - if (newState.deaf) { - await updateUserForPOAP(member, db, channel, false, true).catch(e => LogUtils.logError('failed to capture user joined for poap', e)); - return; - } - const hasJoined: boolean = (newState.channelId === channel.id); - await updateUserForPOAP(member, db, channel, hasJoined).catch(e => LogUtils.logError(`failed to capture user change for POAP hasJoined: ${hasJoined}`, e)); - return; -}; - -export const updateUserForPOAP = async ( - member: GuildMember, db: Db, channel: GuildChannel, hasJoined?: boolean, hasDeafened?: boolean, -): Promise => { - const poapParticipantsDb: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); - const poapParticipant: POAPParticipant = await poapParticipantsDb.findOne({ - discordServerId: `${channel.guild.id}`, - voiceChannelId: `${channel.id}`, - discordUserId: `${member.user.id}`, - }); - - if (hasDeafened) { - Log.debug(`${member.user.tag} | deafened themselves ${channel.name} in ${channel.guild.name}`); - await poapParticipantsDb.deleteOne(poapParticipant).catch(Log.error); - return; - } - const currentDate: Dayjs = dayjs(); - if (!hasJoined) { - Log.debug(`${member.user.tag} | left ${channel.name} in ${channel.guild.name}`); - const startTimeDate: Dayjs = dayjs(poapParticipant.startTime); - let durationInMinutes: number = poapParticipant.durationInMinutes; - if ((currentDate.unix() - startTimeDate.unix() > 0)) { - durationInMinutes += ((currentDate.unix() - startTimeDate.unix()) / 60); - } - await poapParticipantsDb.updateOne(poapParticipant, { - $set: { - endTime: (new Date).toISOString(), - durationInMinutes: durationInMinutes, - }, - }).catch(Log.error); - return; - } - if (poapParticipant !== null && poapParticipant.discordUserId != null && poapParticipant.discordUserId === member.user.id) { - Log.debug(`${member.user.tag} | rejoined ${channel.name} in ${channel.guild.name}`); - await poapParticipantsDb.updateOne(poapParticipant, { - $set: { - startTime: currentDate.toISOString(), - }, - $unset: { - endTime: null, - }, - }).catch(Log.error); - return; - } - - const currentDateStr = (new Date()).toISOString(); - const result: InsertOneWriteOpResult | void = await poapParticipantsDb.insertOne({ - discordUserId: `${member.user.id}`, - discordUserTag: `${member.user.tag}`, - startTime: currentDateStr, - voiceChannelId: `${channel.id}`, - discordServerId: `${channel.guild.id}`, - durationInMinutes: 0, - }).catch(Log.error); - if (result == null || result.insertedCount !== 1) { - throw new MongoError('failed to insert poapParticipant'); - } - Log.debug(`${member.user.tag} | joined ${channel.name} in ${channel.guild.name}`); -}; diff --git a/src/app/events/poap/HandleParticipantDuringEvent.ts b/src/app/events/poap/HandleParticipantDuringEvent.ts new file mode 100644 index 00000000..52914d1e --- /dev/null +++ b/src/app/events/poap/HandleParticipantDuringEvent.ts @@ -0,0 +1,263 @@ +import { GuildChannel, GuildMember, VoiceState } from 'discord.js'; +import { + Collection, + Db, + DeleteWriteOpResultObject, + InsertOneWriteOpResult, + MongoError, +} from 'mongodb'; +import constants from '../../service/constants/constants'; +import { POAPParticipant } from '../../types/poap/POAPParticipant'; +import Log, { LogUtils } from '../../utils/Log'; +import dayjs, { Dayjs } from 'dayjs'; +import MongoDbUtils from '../../utils/MongoDbUtils'; +import { POAPSettings } from '../../types/poap/POAPSettings'; + +const HandleParticipantDuringEvent = async (oldState: VoiceState, newState: VoiceState): Promise => { + if (hasUserBeenDeafened(oldState, newState)) { + if (!await isStateChangeRelatedToActiveEvent(oldState, newState)) { + return; + } + // user has deafened and state change is related to active event + await removeDeafenedUser(oldState, newState); + return; + } + + if (hasUserEnteredChannel(oldState, newState)) { + if (!await isStateChangeRelatedToActiveEvent(oldState, newState)) { + return; + } + await startTrackingUserParticipation(oldState, newState); + return; + } + + // hasUserTriggeredState(oldState, newState); + return; + // if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { + // if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { + // // user did not change channels + // return; + // } + // + // const guild: Guild = (oldState.guild != null) ? oldState.guild : newState.guild; + // const member: GuildMember | null = (oldState.guild != null) ? oldState.member : newState.member; + // + // if (member == null) { + // // could not find member + // return; + // } + // + // const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); + // db.collection(constants.DB_COLLECTION_POAP_SETTINGS); + // + // const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); + // const activeChannelsCursor: Cursor = await poapSettingsDB.find({ + // isActive: true, + // discordServerId: `${guild.id}`, + // }); + // for await (const poapSetting of activeChannelsCursor) { + // const currentDate: Dayjs = dayjs(); + // try { + // const endDate: Dayjs = (poapSetting.endTime == null) ? currentDate : dayjs(poapSetting.endTime); + // if (currentDate.isBefore(endDate)) { + // const voiceChannel: GuildChannel | null = await guild.channels.fetch(poapSetting.voiceChannelId); + // if (voiceChannel == null) { + // Log.warn('voice channel might have been deleted.'); + // return; + // } + // await addUserToDb(oldState, newState, db, voiceChannel, member); + // } else { + // Log.debug(`current date is after or equal to event end date, currentDate: ${currentDate}, endDate: ${endDate}`); + // const poapOrganizerGuildMember: GuildMember = await guild.members.fetch(poapSetting.discordUserId); + // await EndPOAP(poapOrganizerGuildMember, constants.PLATFORM_TYPE_DISCORD); + // } + // } catch (e) { + // LogUtils.logError(`failed to add ${member.user.tag} to db`, e); + // } + // } +}; + +export const addUserToDb = async ( + oldState: VoiceState, newState: VoiceState, db: Db, channel: GuildChannel, member: GuildMember, +): Promise => { + if (!(newState.channelId === channel.id || oldState.channelId === channel.id)) { + // event change is not related to event parameter + return; + } + if (newState.deaf) { + await updateUserForPOAP(member, db, channel, false, true).catch(e => LogUtils.logError('failed to capture user joined for poap', e)); + return; + } + const hasJoined: boolean = (newState.channelId === channel.id); + await updateUserForPOAP(member, db, channel, hasJoined).catch(e => LogUtils.logError(`failed to capture user change for POAP hasJoined: ${hasJoined}`, e)); + return; +}; + +export const updateUserForPOAP = async ( + member: GuildMember, db: Db, channel: GuildChannel, hasJoined?: boolean, hasDeafened?: boolean, +): Promise => { + const poapParticipantsDb: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + const poapParticipant: POAPParticipant = await poapParticipantsDb.findOne({ + discordServerId: `${channel.guild.id}`, + voiceChannelId: `${channel.id}`, + discordUserId: `${member.user.id}`, + }); + + if (hasDeafened) { + Log.debug(`${member.user.tag} | deafened themselves ${channel.name} in ${channel.guild.name}`); + await poapParticipantsDb.deleteOne(poapParticipant).catch(Log.error); + return; + } + const currentDate: Dayjs = dayjs(); + if (!hasJoined) { + Log.debug(`${member.user.tag} | left ${channel.name} in ${channel.guild.name}`); + const startTimeDate: Dayjs = dayjs(poapParticipant.startTime); + let durationInMinutes: number = poapParticipant.durationInMinutes; + if ((currentDate.unix() - startTimeDate.unix() > 0)) { + durationInMinutes += ((currentDate.unix() - startTimeDate.unix()) / 60); + } + await poapParticipantsDb.updateOne(poapParticipant, { + $set: { + endTime: (new Date).toISOString(), + durationInMinutes: durationInMinutes, + }, + }).catch(Log.error); + return; + } + if (poapParticipant !== null && poapParticipant.discordUserId != null && poapParticipant.discordUserId === member.user.id) { + Log.debug(`${member.user.tag} | rejoined ${channel.name} in ${channel.guild.name}`); + await poapParticipantsDb.updateOne(poapParticipant, { + $set: { + startTime: currentDate.toISOString(), + }, + $unset: { + endTime: null, + }, + }).catch(Log.error); + return; + } + + const currentDateStr = (new Date()).toISOString(); + const result: InsertOneWriteOpResult | void = await poapParticipantsDb.insertOne({ + discordUserId: `${member.user.id}`, + discordUserTag: `${member.user.tag}`, + startTime: currentDateStr, + voiceChannelId: `${channel.id}`, + discordServerId: `${channel.guild.id}`, + durationInMinutes: 0, + }).catch(Log.error); + if (result == null || result.insertedCount !== 1) { + throw new MongoError('failed to insert poapParticipant'); + } + Log.debug(`${member.user.tag} | joined ${channel.name} in ${channel.guild.name}`); +}; + +const hasUserBeenDeafened = (oldState: VoiceState, newState: VoiceState): boolean => { + return newState.deaf != null && newState.deaf && newState.deaf != oldState.deaf; +}; + +const hasUserEnteredChannel = (oldState: VoiceState, newState: VoiceState): boolean => { + return newState.channelId != null && newState.channelId != oldState.channelId; +}; + +const isStateChangeRelatedToActiveEvent = async (oldState: VoiceState, newState: VoiceState): Promise => { + const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); + const channelIdA = oldState.channelId; + const channelIdB = newState.channelId; + const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); + + if (channelIdA != null && channelIdA != '' && channelIdB != null && channelIdB != '' && channelIdA == channelIdB) { + const activeEvent: POAPSettings | null = await poapSettingsDB.findOne({ + isActive: true, + voiceChannelId: channelIdA.toString(), + discordServerId: oldState.guild.id.toString(), + }); + + if (activeEvent != null) { + Log.debug(`state changed related to active event, userId: ${oldState.id}, channelId: ${channelIdA}`); + return true; + } + } + + if (channelIdB != null) { + const activeEvent: POAPSettings | null = await poapSettingsDB.findOne({ + isActive: true, + voiceChannelId: channelIdB.toString(), + discordServerId: newState.guild.id.toString(), + }); + + if (activeEvent != null) { + Log.debug(`state changed related to active event, userId: ${newState.id}, channelId: ${channelIdB}`); + return true; + } + } + + Log.debug('state change not related to event'); + return false; +}; + +const removeDeafenedUser = async (oldState: VoiceState, newState: VoiceState) => { + const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); + const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + + if (oldState.channelId) { + const result: DeleteWriteOpResultObject | void = await poapSettingsDB.deleteOne({ + isActive: true, + voiceChannelId: oldState.channelId.toString(), + discordServerId: oldState.guild.id.toString(), + discordUserId: oldState.id.toString(), + }).catch(Log.warn); + if (result != null && result.deletedCount == 1) { + Log.debug(`user removed from db, userId: ${oldState.id} deafened themselves, channelId: ${oldState.channelId}, discordServerId: ${oldState.id}`); + } + } + + if (newState.channelId) { + const result: DeleteWriteOpResultObject | void = await poapSettingsDB.deleteOne({ + isActive: true, + voiceChannelId: newState.channelId.toString(), + discordServerId: newState.guild.id.toString(), + discordUserId: newState.id.toString(), + }).catch(Log.warn); + if (result != null && result.deletedCount == 1) { + Log.debug(`user removed from db, userId: ${newState.id} deafened themselves, channelId: ${newState.channelId}, discordServerId: ${newState.id}`); + } + } +}; + +const startTrackingUserParticipation = async (oldState: VoiceState, newState: VoiceState) => { + +}; + +const stopTrackingUserParticipation = async (oldState: VoiceState, newState: VoiceState) => { + +}; + +const hasUserTriggeredState = (oldState: VoiceState, newState: VoiceState): boolean => { + // Check if user entered a channel + if (newState.channelId != null && newState.channelId != oldState.channelId) { + // Log.debug('entered a channel'); + return true; + } + + // Check if user left all channels + if (newState.channelId == null && newState.channelId != oldState.channelId) { + // Log.debug('left all channels'); + return true; + } + + return false; + // if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { + // // user did not change channels + // return false; + // } + // const member: GuildMember | null = (oldState.guild != null) ? oldState.member : newState.member; + // + // if (member == null) { + // // could not find member + // return false; + // } + // return true; +}; + +export default HandleParticipantDuringEvent; \ No newline at end of file diff --git a/src/app/service/poap/start/StartPOAP.ts b/src/app/service/poap/start/StartPOAP.ts index 8ad61322..e90270f0 100644 --- a/src/app/service/poap/start/StartPOAP.ts +++ b/src/app/service/poap/start/StartPOAP.ts @@ -23,7 +23,6 @@ import { import constants from '../../constants/constants'; import { POAPSettings } from '../../../types/poap/POAPSettings'; import ValidationError from '../../../errors/ValidationError'; -import { updateUserForPOAP } from '../../../events/poap/AddUserForEvent'; import ServiceUtils from '../../../utils/ServiceUtils'; import EarlyTermination from '../../../errors/EarlyTermination'; import POAPUtils from '../../../utils/POAPUtils'; @@ -35,6 +34,7 @@ import MongoDbUtils from '../../../utils/MongoDbUtils'; import StartTwitterFlow from './StartTwitterFlow'; import StartChannelFlow from './StartChannelFlow'; import channelIds from '../../constants/channelIds'; +import { updateUserForPOAP } from '../../../events/poap/HandleParticipantDuringEvent'; export default async (ctx: CommandContext, guildMember: GuildMember, platform: string, event: string, duration?: number): Promise => { if (ctx.guildID == undefined) { From 8be6b14ae14b0633c892bc40b3dd3c0a57568323 Mon Sep 17 00:00:00 2001 From: Brian Date: Sat, 8 Jan 2022 23:08:16 -0500 Subject: [PATCH 02/20] set year as number --- src/app/api/poap/EventsAPI.ts | 2 +- src/app/api/types/poap-events/EventsRequestType.ts | 2 +- src/app/utils/POAPUtils.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/api/poap/EventsAPI.ts b/src/app/api/poap/EventsAPI.ts index 13ea23f8..6b89cb99 100644 --- a/src/app/api/poap/EventsAPI.ts +++ b/src/app/api/poap/EventsAPI.ts @@ -54,7 +54,7 @@ const EventsAPI = { start_date: `${request.start_date}`, end_date: `${request.end_date}`, expiry_date: `${request.expiry_date}`, - year: `${request.year}`, + year: request.year, event_url: `${request.event_url}`, virtual_event: `${request.virtual_event}`, secret_code: `${request.secret_code}`, diff --git a/src/app/api/types/poap-events/EventsRequestType.ts b/src/app/api/types/poap-events/EventsRequestType.ts index b99a91d8..ba9eba67 100644 --- a/src/app/api/types/poap-events/EventsRequestType.ts +++ b/src/app/api/types/poap-events/EventsRequestType.ts @@ -8,7 +8,7 @@ export type EventsRequestType = { start_date: string, end_date: string, expiry_date: string, - year: string, + year: number, event_url?: string, virtual_event: boolean, image: AxiosResponse, diff --git a/src/app/utils/POAPUtils.ts b/src/app/utils/POAPUtils.ts index 717b0532..0a1b4712 100644 --- a/src/app/utils/POAPUtils.ts +++ b/src/app/utils/POAPUtils.ts @@ -654,8 +654,8 @@ const POAPUtils = { } }, - getEventYear(startDateObj: Dayjs): string { - return startDateObj.year().toString(); + getEventYear(startDateObj: Dayjs): number { + return startDateObj.year(); }, }; From 0a6328e7350bd1ad47190718abfb4fc00cba97c9 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 9 Jan 2022 11:22:25 -0500 Subject: [PATCH 03/20] fix gm regex --- CHANGELOG.md | 4 ++ src/app/events/chat/HandlePOAPGM.ts | 2 +- .../poap/HandleParticipantDuringEvent.ts | 70 +++++++++---------- src/app/service/poap/SchedulePOAP.ts | 4 +- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85923756..6e097c12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ ## 2.6.0-SNAPSHOT 1. Stability check + - add sentry github action + - fix poap mint api call + - fix auto end for DM event + - fix gm regex for single line ## 2.5.2-RELEASE diff --git a/src/app/events/chat/HandlePOAPGM.ts b/src/app/events/chat/HandlePOAPGM.ts index cdfdc0b9..89cd3601 100644 --- a/src/app/events/chat/HandlePOAPGM.ts +++ b/src/app/events/chat/HandlePOAPGM.ts @@ -15,7 +15,7 @@ const HandlePOAPGM = async (message: Message): Promise => { return; } - if (!content.match(/gm/gi)) { + if (!content.match(/^gm$/gi)) { return; } Log.debug(`found gm from ${message.author.tag}`); diff --git a/src/app/events/poap/HandleParticipantDuringEvent.ts b/src/app/events/poap/HandleParticipantDuringEvent.ts index 52914d1e..691fa181 100644 --- a/src/app/events/poap/HandleParticipantDuringEvent.ts +++ b/src/app/events/poap/HandleParticipantDuringEvent.ts @@ -27,7 +27,7 @@ const HandleParticipantDuringEvent = async (oldState: VoiceState, newState: Voic if (!await isStateChangeRelatedToActiveEvent(oldState, newState)) { return; } - await startTrackingUserParticipation(oldState, newState); + // await startTrackingUserParticipation(oldState, newState); return; } @@ -225,39 +225,39 @@ const removeDeafenedUser = async (oldState: VoiceState, newState: VoiceState) => } }; -const startTrackingUserParticipation = async (oldState: VoiceState, newState: VoiceState) => { - -}; - -const stopTrackingUserParticipation = async (oldState: VoiceState, newState: VoiceState) => { - -}; - -const hasUserTriggeredState = (oldState: VoiceState, newState: VoiceState): boolean => { - // Check if user entered a channel - if (newState.channelId != null && newState.channelId != oldState.channelId) { - // Log.debug('entered a channel'); - return true; - } - - // Check if user left all channels - if (newState.channelId == null && newState.channelId != oldState.channelId) { - // Log.debug('left all channels'); - return true; - } - - return false; - // if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { - // // user did not change channels - // return false; - // } - // const member: GuildMember | null = (oldState.guild != null) ? oldState.member : newState.member; - // - // if (member == null) { - // // could not find member - // return false; - // } - // return true; -}; +// const startTrackingUserParticipation = async (oldState: VoiceState, newState: VoiceState) => { +// +// }; +// +// const stopTrackingUserParticipation = async (oldState: VoiceState, newState: VoiceState) => { +// +// }; +// +// const hasUserTriggeredState = (oldState: VoiceState, newState: VoiceState): boolean => { +// // Check if user entered a channel +// if (newState.channelId != null && newState.channelId != oldState.channelId) { +// // Log.debug('entered a channel'); +// return true; +// } +// +// // Check if user left all channels +// if (newState.channelId == null && newState.channelId != oldState.channelId) { +// // Log.debug('left all channels'); +// return true; +// } +// +// return false; +// // if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { +// // // user did not change channels +// // return false; +// // } +// // const member: GuildMember | null = (oldState.guild != null) ? oldState.member : newState.member; +// // +// // if (member == null) { +// // // could not find member +// // return false; +// // } +// // return true; +// }; export default HandleParticipantDuringEvent; \ No newline at end of file diff --git a/src/app/service/poap/SchedulePOAP.ts b/src/app/service/poap/SchedulePOAP.ts index 5e407196..3e166ad5 100644 --- a/src/app/service/poap/SchedulePOAP.ts +++ b/src/app/service/poap/SchedulePOAP.ts @@ -23,7 +23,7 @@ import { MessageOptions as MessageOptionsSlash } from 'slash-create/lib/structur const SchedulePOAP = async (ctx: CommandContext, guildMember: GuildMember, numberToMint: number): Promise => { if (ctx.guildID == undefined) { - await ctx.send('Please try schedule within discord channel'); + await ctx.send('Please try poap mint within discord channel'); return; } const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Minting POAPs is always super exciting!'); @@ -51,7 +51,7 @@ const SchedulePOAP = async (ctx: CommandContext, guildMember: GuildMember, numbe if (isDmOn) { await guildMember.send(msg1); - await ctx.sendFollowUp('I just sent you a DM!'); + await ctx.send({ content: 'I just sent you a DM!', ephemeral: true }); } else if (ctx) { await ctx.sendFollowUp(msg1); } From 56560611397813a9a04803deed98e0b6ee8f72fd Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 9 Jan 2022 11:36:35 -0500 Subject: [PATCH 04/20] add sentry github action --- .github/workflows/deploy-prod.yml | 10 ++++++++++ .github/workflows/deploy-qa.yml | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index d4c07f00..7f17207e 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -47,3 +47,13 @@ jobs: HD_POAP_CLIENT_ID: ${{secrets.PROD_POAP_CLIENT_ID}} HD_POAP_CLIENT_SECRET: ${{secrets.PROD_POAP_CLIENT_SECRET}} HD_SENTRY_IO_DSN: ${{secrets.PROD_SENTRY_IO_DSN}} + - name: Sentry Release + uses: getsentry/action-release@v1.1.6 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + sourcemaps: './dist' + with: + environment: production + diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 4151f6db..7ae1f982 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -46,4 +46,13 @@ jobs: HD_TWITTER_API_SECRET: ${{secrets.QA_TWITTER_API_SECRET}} HD_TWITTER_BEARER_TOKEN: ${{secrets.QA_TWITTER_API_BEARER_TOKEN}} HD_TWITTER_ACCESS_TOKEN_SECRET: ${{secrets.QA_TWITTER_API_ACCESS_TOKEN_SECRET}} - HD_SENTRY_IO_DSN: ${{secrets.QA_SENTRY_IO_DSN}} \ No newline at end of file + HD_SENTRY_IO_DSN: ${{secrets.QA_SENTRY_IO_DSN}} + - name: Sentry Release + uses: getsentry/action-release@v1.1.6 + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + sourcemaps: './dist' + with: + environment: qa \ No newline at end of file From efb165c950bff2383a7b98ea7a95beaacd12f2a9 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 9 Jan 2022 13:08:09 -0500 Subject: [PATCH 05/20] fix autoend for DMs --- .github/workflows/deploy-prod.yml | 2 +- .github/workflows/deploy-qa.yml | 4 +++- src/app/events/Ready.ts | 5 +++-- src/app/service/poap/POAPService.ts | 7 ++++--- src/app/service/poap/end/EndPOAP.ts | 5 +++-- src/app/service/poap/end/EndTwitterFlow.ts | 3 ++- src/app/service/poap/start/StartTwitterFlow.ts | 2 +- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 7f17207e..fc54037c 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -55,5 +55,5 @@ jobs: SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} sourcemaps: './dist' with: - environment: production + environment: 'production' diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 7ae1f982..676e52e3 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -4,6 +4,7 @@ on: branches: - 'develop' - 'hotfix/**' + - 'feature/stability-check' pull_request: branches: - 'release/**' @@ -55,4 +56,5 @@ jobs: SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} sourcemaps: './dist' with: - environment: qa \ No newline at end of file + environment: 'qa' + version: 'degen@2.6.0' \ No newline at end of file diff --git a/src/app/events/Ready.ts b/src/app/events/Ready.ts index f47b83af..a64e40aa 100644 --- a/src/app/events/Ready.ts +++ b/src/app/events/Ready.ts @@ -26,8 +26,9 @@ export default class implements DiscordEvent { } const db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); await updateActiveDiscordServers(client, db); - await POAPService.runAutoEndSetup(client, constants.PLATFORM_TYPE_DISCORD).catch(Log.error); - await POAPService.runAutoEndSetup(client, constants.PLATFORM_TYPE_TWITTER).catch(Log.error); + // should not wait + POAPService.runAutoEndSetup(client, constants.PLATFORM_TYPE_DISCORD).catch(Log.error); + POAPService.runAutoEndSetup(client, constants.PLATFORM_TYPE_TWITTER).catch(Log.error); await POAPService.clearExpiredPOAPs(); Log.info(`${constants.APP_NAME} is ready!`); diff --git a/src/app/service/poap/POAPService.ts b/src/app/service/poap/POAPService.ts index f97feee5..8bd98bf8 100644 --- a/src/app/service/poap/POAPService.ts +++ b/src/app/service/poap/POAPService.ts @@ -46,9 +46,9 @@ const POAPService = { for (const expiredEvent of expiredEventsList) { const poapGuild: Guild = await client.guilds.fetch(expiredEvent.discordServerId); const poapOrganizer: GuildMember = await poapGuild.members.fetch(expiredEvent.discordUserId); - EndPOAP(poapOrganizer, platform).catch(Log.error); + await EndPOAP(poapOrganizer, platform).catch(Log.error); } - Log.debug(`all expired events ended for ${platform}`); + Log.debug(`all expired events ended for ${platform}, now checking for active events`); const poapSettingsActiveEventsCursor: Cursor = await poapSettingsDB.find({ isActive: true, endTime: { @@ -62,6 +62,7 @@ const POAPService = { }); Log.debug(`found ${activeEventsList.length} active events for ${platform}`); + // Skip twitter active event check (since it uses a participant check in system) for (const activeEvent of activeEventsList) { if (platform == constants.PLATFORM_TYPE_DISCORD) { try { @@ -70,7 +71,7 @@ const POAPService = { if (!channelChoice) { throw new ValidationError('Missing channel'); } - await storePresentMembers(db, channelChoice).catch(); + await storePresentMembers(db, channelChoice).catch(Log.error); } catch (e) { LogUtils.logError('failed trying to store present members for active poap event', e); } diff --git a/src/app/service/poap/end/EndPOAP.ts b/src/app/service/poap/end/EndPOAP.ts index b6f832b5..7a2f8eed 100644 --- a/src/app/service/poap/end/EndPOAP.ts +++ b/src/app/service/poap/end/EndPOAP.ts @@ -16,6 +16,7 @@ import MongoDbUtils from '../../../utils/MongoDbUtils'; import ServiceUtils from '../../../utils/ServiceUtils'; import EndTwitterFlow from './EndTwitterFlow'; import { POAPDistributionResults } from '../../../types/poap/POAPDistributionResults'; +import channelIds from '../../constants/channelIds'; export default async (guildMember: GuildMember, platform: string, ctx?: CommandContext): Promise => { Log.debug('attempting to end poap event'); @@ -47,14 +48,14 @@ export default async (guildMember: GuildMember, platform: string, ctx?: CommandC Log.debug('active poap event found'); - const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Over already? Can\'t wait for the next one'); + const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Hello! I found a poap event, let me try ending it.'); let channelExecution: TextChannel | null = null; if (!isDmOn && ctx) { await ctx.send({ content: '⚠ Please make sure this is a private channel. I can help you distribute POAPs but anyone who has access to this channel can see the POAP links! ⚠', ephemeral: true }); } else if (ctx) { await ctx.send({ content: 'Please check your DMs!', ephemeral: true }); - } else { + } else if (poapSettingsDoc.channelExecutionId != channelIds.DM) { if (poapSettingsDoc.channelExecutionId == null || poapSettingsDoc.channelExecutionId == '') { Log.debug(`channelExecutionId missing for ${guildMember.user.tag}, ${guildMember.user.id}, skipping poap end for expired event`); return; diff --git a/src/app/service/poap/end/EndTwitterFlow.ts b/src/app/service/poap/end/EndTwitterFlow.ts index e1fe8c94..ed7f9a3e 100644 --- a/src/app/service/poap/end/EndTwitterFlow.ts +++ b/src/app/service/poap/end/EndTwitterFlow.ts @@ -18,6 +18,7 @@ import dayjs, { Dayjs } from 'dayjs'; import POAPUtils, { TwitterPOAPFileParticipant } from '../../../utils/POAPUtils'; import { Buffer } from 'buffer'; import { POAPDistributionResults } from '../../../types/poap/POAPDistributionResults'; +import channelIds from '../../constants/channelIds'; const EndTwitterFlow = async (guildMember: GuildMember, db: Db, ctx?: CommandContext): Promise => { Log.debug('starting twitter poap end flow...'); @@ -42,7 +43,7 @@ const EndTwitterFlow = async (guildMember: GuildMember, db: Db, ctx?: CommandCon await ctx.send({ content: '⚠ Please make sure this is a private channel. I can help you distribute POAPs but anyone who has access to this channel can see the POAP links! ⚠', ephemeral: true }); } else if (ctx) { await ctx.send({ content: 'Please check your DMs!', ephemeral: true }); - } else { + } else if (activeTwitterSettings.channelExecutionId != channelIds.DM) { if (activeTwitterSettings.channelExecutionId == null || activeTwitterSettings.channelExecutionId == '') { Log.debug(`channelExecutionId missing for ${guildMember.user.tag}, ${guildMember.user.id}, skipping poap end for expired event`); return; diff --git a/src/app/service/poap/start/StartTwitterFlow.ts b/src/app/service/poap/start/StartTwitterFlow.ts index 6be47559..dee73eb6 100644 --- a/src/app/service/poap/start/StartTwitterFlow.ts +++ b/src/app/service/poap/start/StartTwitterFlow.ts @@ -21,7 +21,7 @@ const StartTwitterFlow = async (ctx: CommandContext, guildMember: GuildMember, d return; } const twitterClientV2: TwitterApi = new TwitterApi(apiKeys.twitterBearerToken as string); - const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Oh yea, time for a POAP event!...'); + const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Hello! I can help start a POAP event!'); let twitterSpaceResult: SpaceV2LookupResult | null = null; try { From ce831d7c8070b89e795966ffc4bdec7571bf2fcc Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 9 Jan 2022 13:15:15 -0500 Subject: [PATCH 06/20] dynamically add package version --- .github/workflows/deploy-prod.yml | 4 ++++ .github/workflows/deploy-qa.yml | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index fc54037c..c356540e 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -47,6 +47,9 @@ jobs: HD_POAP_CLIENT_ID: ${{secrets.PROD_POAP_CLIENT_ID}} HD_POAP_CLIENT_SECRET: ${{secrets.PROD_POAP_CLIENT_SECRET}} HD_SENTRY_IO_DSN: ${{secrets.PROD_SENTRY_IO_DSN}} + - name: Get current package version + id: package-version + uses: martinbeentjes/npm-get-version-action@v1.1.0 - name: Sentry Release uses: getsentry/action-release@v1.1.6 env: @@ -56,4 +59,5 @@ jobs: sourcemaps: './dist' with: environment: 'production' + version: 'degen@'${{ steps.package-version.outputs.current-version }} diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 676e52e3..e5a26f1e 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -48,6 +48,9 @@ jobs: HD_TWITTER_BEARER_TOKEN: ${{secrets.QA_TWITTER_API_BEARER_TOKEN}} HD_TWITTER_ACCESS_TOKEN_SECRET: ${{secrets.QA_TWITTER_API_ACCESS_TOKEN_SECRET}} HD_SENTRY_IO_DSN: ${{secrets.QA_SENTRY_IO_DSN}} + - name: Get current package version + id: package-version + uses: martinbeentjes/npm-get-version-action@v1.1.0 - name: Sentry Release uses: getsentry/action-release@v1.1.6 env: @@ -57,4 +60,4 @@ jobs: sourcemaps: './dist' with: environment: 'qa' - version: 'degen@2.6.0' \ No newline at end of file + version: 'degen@'${{ steps.package-version.outputs.current-version }} \ No newline at end of file From 6ba86be41c618ddbc9f36820396d1697f793c6a3 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 9 Jan 2022 13:23:51 -0500 Subject: [PATCH 07/20] include sources map --- .github/workflows/deploy-prod.yml | 2 +- .github/workflows/deploy-qa.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index c356540e..85a3ed59 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -60,4 +60,4 @@ jobs: with: environment: 'production' version: 'degen@'${{ steps.package-version.outputs.current-version }} - + sourcemaps: './dist' diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index e5a26f1e..39b3eecd 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -57,7 +57,7 @@ jobs: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ secrets.SENTRY_ORG }} SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} - sourcemaps: './dist' with: environment: 'qa' - version: 'degen@'${{ steps.package-version.outputs.current-version }} \ No newline at end of file + version: 'degen@'${{ steps.package-version.outputs.current-version }} + sourcemaps: './dist' \ No newline at end of file From 4c19143f5a20450aa611628d094d6af033d88c5c Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 9 Jan 2022 13:29:39 -0500 Subject: [PATCH 08/20] test --- .github/workflows/deploy-prod.yml | 2 +- .github/workflows/deploy-qa.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 85a3ed59..5e10587d 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -59,5 +59,5 @@ jobs: sourcemaps: './dist' with: environment: 'production' - version: 'degen@'${{ steps.package-version.outputs.current-version }} + version: 'degen@2.6.0' sourcemaps: './dist' diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 39b3eecd..7997a3ef 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -59,5 +59,5 @@ jobs: SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} with: environment: 'qa' - version: 'degen@'${{ steps.package-version.outputs.current-version }} - sourcemaps: './dist' \ No newline at end of file + version: 'degen@2.6.0' + sourcemaps: './dist' From b9c0b8bd077b00488bef5f671425c9ff5676661e Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 9 Jan 2022 13:34:07 -0500 Subject: [PATCH 09/20] fix --- .github/workflows/deploy-prod.yml | 2 +- .github/workflows/deploy-qa.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 5e10587d..6435a1aa 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -59,5 +59,5 @@ jobs: sourcemaps: './dist' with: environment: 'production' - version: 'degen@2.6.0' + version: 'degen@${{ steps.package-version.outputs.current-version }}' sourcemaps: './dist' diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 7997a3ef..bb412a60 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -59,5 +59,5 @@ jobs: SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} with: environment: 'qa' - version: 'degen@2.6.0' - sourcemaps: './dist' + version: 'degen@${{ steps.package-version.outputs.current-version }}' + sourcemaps: './dist' \ No newline at end of file From 7f52bbe3cafa20c71ad1c48b6d80434ddda4d7b0 Mon Sep 17 00:00:00 2001 From: Brian Date: Sun, 9 Jan 2022 13:59:38 -0500 Subject: [PATCH 10/20] complete sentry sourcemaps integration --- .github/workflows/deploy-qa.yml | 1 - src/app/app.ts | 2 +- src/app/events/chat/HandlePOAPGM.ts | 1 + src/app/service/poap/OptInPOAP.ts | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index bb412a60..54f1e3ca 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -4,7 +4,6 @@ on: branches: - 'develop' - 'hotfix/**' - - 'feature/stability-check' pull_request: branches: - 'release/**' diff --git a/src/app/app.ts b/src/app/app.ts index 57b32c93..aa82f01d 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -83,7 +83,7 @@ function initializeSentryIO() { dsn: `${apiKeys.sentryDSN}`, tracesSampleRate: 1.0, release: `${constants.APP_NAME}@${constants.APP_VERSION}`, - environment: process.env.SENTRY_ENVIRONMENT, + environment: `${process.env.SENTRY_ENVIRONMENT}`, integrations: [ new RewriteFrames({ root: __dirname, diff --git a/src/app/events/chat/HandlePOAPGM.ts b/src/app/events/chat/HandlePOAPGM.ts index 89cd3601..1ce1b89f 100644 --- a/src/app/events/chat/HandlePOAPGM.ts +++ b/src/app/events/chat/HandlePOAPGM.ts @@ -18,6 +18,7 @@ const HandlePOAPGM = async (message: Message): Promise => { if (!content.match(/^gm$/gi)) { return; } + Log.debug(`found gm from ${message.author.tag}`); const dmChannel: DMChannel = message.channel as DMChannel; diff --git a/src/app/service/poap/OptInPOAP.ts b/src/app/service/poap/OptInPOAP.ts index 1426db3f..076d7a4b 100644 --- a/src/app/service/poap/OptInPOAP.ts +++ b/src/app/service/poap/OptInPOAP.ts @@ -92,6 +92,7 @@ const OptInPOAP = async (user: User, dmChannel: DMChannel): Promise => { } Log.debug('user settings update skipped'); } else { + Log.debug(`user is opted in to dms, userId: ${user.id}`); await dmChannel.send({ content: 'I will send you POAPs as soon as I get them!' }); } }; From 3c05aafdd7e8f6c211a7b11aab28f9009f61b69f Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 10 Jan 2022 11:03:31 -0500 Subject: [PATCH 11/20] handle start/stop poap tracking and deafened users --- src/app/events/VoiceStateUpdate.ts | 2 +- .../poap/HandleParticipantDuringEvent.ts | 366 ++++++++---------- src/app/service/poap/end/EndPOAP.ts | 55 ++- src/app/service/poap/start/StartPOAP.ts | 32 +- src/app/types/poap/POAPParticipant.ts | 2 +- src/app/utils/POAPUtils.ts | 55 +-- 6 files changed, 232 insertions(+), 280 deletions(-) diff --git a/src/app/events/VoiceStateUpdate.ts b/src/app/events/VoiceStateUpdate.ts index b5445576..ddc33ee8 100644 --- a/src/app/events/VoiceStateUpdate.ts +++ b/src/app/events/VoiceStateUpdate.ts @@ -18,7 +18,7 @@ export default class implements DiscordEvent { */ async execute(oldState: VoiceState, newState: VoiceState): Promise { try { - await HandleParticipantDuringEvent(oldState, newState).catch(e => LogUtils.logError('failed to add user for POAP event', e, oldState.guild.id)); + await HandleParticipantDuringEvent(oldState, newState).catch(e => LogUtils.logError('failed to handle user in POAP event', e, oldState.guild.id)); } catch (e) { LogUtils.logError('failed to process event voiceStateUpdate', e); } diff --git a/src/app/events/poap/HandleParticipantDuringEvent.ts b/src/app/events/poap/HandleParticipantDuringEvent.ts index 691fa181..12b34571 100644 --- a/src/app/events/poap/HandleParticipantDuringEvent.ts +++ b/src/app/events/poap/HandleParticipantDuringEvent.ts @@ -1,263 +1,213 @@ -import { GuildChannel, GuildMember, VoiceState } from 'discord.js'; +import { + VoiceState, +} from 'discord.js'; import { Collection, Db, DeleteWriteOpResultObject, InsertOneWriteOpResult, MongoError, + UpdateWriteOpResult, } from 'mongodb'; import constants from '../../service/constants/constants'; import { POAPParticipant } from '../../types/poap/POAPParticipant'; -import Log, { LogUtils } from '../../utils/Log'; +import Log from '../../utils/Log'; import dayjs, { Dayjs } from 'dayjs'; import MongoDbUtils from '../../utils/MongoDbUtils'; import { POAPSettings } from '../../types/poap/POAPSettings'; +type BasicUser = { + id: string; + tag: string | undefined; +} + const HandleParticipantDuringEvent = async (oldState: VoiceState, newState: VoiceState): Promise => { if (hasUserBeenDeafened(oldState, newState)) { - if (!await isStateChangeRelatedToActiveEvent(oldState, newState)) { - return; + Log.log(`user has deafened, userId: ${newState.id}`); + if (await isChannelActivePOAPEvent(oldState.channelId, oldState.guild.id)) { + await removeDeafenedUser(oldState.channelId, oldState.guild.id, oldState.id); + } + if (newState.channelId != oldState.channelId && await isChannelActivePOAPEvent(newState.channelId, newState.guild.id)) { + await removeDeafenedUser(newState.channelId, newState.guild.id, newState.id); } - // user has deafened and state change is related to active event - await removeDeafenedUser(oldState, newState); return; } - if (hasUserEnteredChannel(oldState, newState)) { - if (!await isStateChangeRelatedToActiveEvent(oldState, newState)) { - return; + if (hasUserBeenUnDeafened(oldState, newState)) { + Log.log(`user has undeafened, userId: ${newState.id}`); + if (await isChannelActivePOAPEvent(newState.channelId, newState.guild.id)) { + await startTrackingUserParticipation({ id: newState.id, tag: newState.member?.user.tag }, newState.guild.id, newState.channelId); } - // await startTrackingUserParticipation(oldState, newState); + } + + if (isUserDeaf(newState)) { + Log.log(`user is deaf, userId: ${newState.id}`); return; } - // hasUserTriggeredState(oldState, newState); - return; - // if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { - // if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { - // // user did not change channels - // return; - // } - // - // const guild: Guild = (oldState.guild != null) ? oldState.guild : newState.guild; - // const member: GuildMember | null = (oldState.guild != null) ? oldState.member : newState.member; - // - // if (member == null) { - // // could not find member - // return; - // } - // - // const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); - // db.collection(constants.DB_COLLECTION_POAP_SETTINGS); - // - // const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); - // const activeChannelsCursor: Cursor = await poapSettingsDB.find({ - // isActive: true, - // discordServerId: `${guild.id}`, - // }); - // for await (const poapSetting of activeChannelsCursor) { - // const currentDate: Dayjs = dayjs(); - // try { - // const endDate: Dayjs = (poapSetting.endTime == null) ? currentDate : dayjs(poapSetting.endTime); - // if (currentDate.isBefore(endDate)) { - // const voiceChannel: GuildChannel | null = await guild.channels.fetch(poapSetting.voiceChannelId); - // if (voiceChannel == null) { - // Log.warn('voice channel might have been deleted.'); - // return; - // } - // await addUserToDb(oldState, newState, db, voiceChannel, member); - // } else { - // Log.debug(`current date is after or equal to event end date, currentDate: ${currentDate}, endDate: ${endDate}`); - // const poapOrganizerGuildMember: GuildMember = await guild.members.fetch(poapSetting.discordUserId); - // await EndPOAP(poapOrganizerGuildMember, constants.PLATFORM_TYPE_DISCORD); - // } - // } catch (e) { - // LogUtils.logError(`failed to add ${member.user.tag} to db`, e); - // } - // } + if (hasUserChangedChannels(oldState, newState)) { + if (await isChannelActivePOAPEvent(oldState.channelId, oldState.guild.id)) { + await stopTrackingUserParticipation({ id: oldState.id, tag: oldState.member?.user.tag }, oldState.guild.id, oldState.channelId, null); + } + if (await isChannelActivePOAPEvent(newState.channelId, newState.guild.id)) { + await startTrackingUserParticipation({ id: newState.id, tag: newState.member?.user.tag }, newState.guild.id, newState.channelId); + } + } + }; -export const addUserToDb = async ( - oldState: VoiceState, newState: VoiceState, db: Db, channel: GuildChannel, member: GuildMember, -): Promise => { - if (!(newState.channelId === channel.id || oldState.channelId === channel.id)) { - // event change is not related to event parameter - return; +const hasUserBeenDeafened = (oldState: VoiceState, newState: VoiceState): boolean => { + return newState.deaf != null && newState.deaf && newState.deaf != oldState.deaf; +}; + +const hasUserBeenUnDeafened = (oldState: VoiceState, newState: VoiceState): boolean => { + return newState.deaf != null && !newState.deaf && newState.deaf != oldState.deaf; +}; + +const isUserDeaf = (newState: VoiceState): boolean => { + return newState.deaf !== null && newState.deaf; +}; + +const hasUserChangedChannels = (oldState: VoiceState, newState: VoiceState): boolean => { + return newState.channelId != oldState.channelId; +}; + +const isChannelActivePOAPEvent = async ( + channelId: string | null, guildId: string | null, +): Promise => { + if (channelId == null || guildId == null) { + return false; } - if (newState.deaf) { - await updateUserForPOAP(member, db, channel, false, true).catch(e => LogUtils.logError('failed to capture user joined for poap', e)); - return; + + const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); + const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); + const activeEvent: POAPSettings | null = await poapSettingsDB.findOne({ + isActive: true, + voiceChannelId: channelId, + discordServerId: guildId, + }); + + if (activeEvent != null) { + // Log.debug(`channel is active, channelId: ${channelId}, guildId: ${guildId}`); + return true; } - const hasJoined: boolean = (newState.channelId === channel.id); - await updateUserForPOAP(member, db, channel, hasJoined).catch(e => LogUtils.logError(`failed to capture user change for POAP hasJoined: ${hasJoined}`, e)); - return; + + // Log.debug('channel not active'); + return false; }; -export const updateUserForPOAP = async ( - member: GuildMember, db: Db, channel: GuildChannel, hasJoined?: boolean, hasDeafened?: boolean, -): Promise => { - const poapParticipantsDb: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); - const poapParticipant: POAPParticipant = await poapParticipantsDb.findOne({ - discordServerId: `${channel.guild.id}`, - voiceChannelId: `${channel.id}`, - discordUserId: `${member.user.id}`, - }); +const removeDeafenedUser = async (channelId: string | null, guildId: string | null, userId: string | null) => { + if (channelId == null || guildId == null || userId == null) { + return; + } - if (hasDeafened) { - Log.debug(`${member.user.tag} | deafened themselves ${channel.name} in ${channel.guild.name}`); - await poapParticipantsDb.deleteOne(poapParticipant).catch(Log.error); + const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); + const poapParticipantsCol: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + + const result: DeleteWriteOpResultObject | void = await poapParticipantsCol.deleteOne({ + voiceChannelId: channelId, + discordServerId: guildId, + discordUserId: userId, + }).catch(Log.warn); + if (result != null && result.deletedCount == 1) { + Log.debug(`user deafened themselves and removed from db, userId: ${userId}, channelId: ${channelId}, discordServerId: ${guildId}`); return; } - const currentDate: Dayjs = dayjs(); - if (!hasJoined) { - Log.debug(`${member.user.tag} | left ${channel.name} in ${channel.guild.name}`); - const startTimeDate: Dayjs = dayjs(poapParticipant.startTime); - let durationInMinutes: number = poapParticipant.durationInMinutes; - if ((currentDate.unix() - startTimeDate.unix() > 0)) { - durationInMinutes += ((currentDate.unix() - startTimeDate.unix()) / 60); + Log.debug('deafened user not removed/found in any active channels'); +}; + +export const startTrackingUserParticipation = async (user: BasicUser, guildId: string, channelId: string | null): Promise => { + const participant = await retrieveActiveParticipant(user, channelId, guildId); + + channelId = channelId as string; + guildId = guildId as string; + + const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); + const poapParticipantsDb: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + if (participant == null) { + const userTag: string = user.tag ? user.tag : ''; + const resultInsert: InsertOneWriteOpResult | void = await poapParticipantsDb.insertOne({ + discordUserId: user.id, + discordUserTag: userTag, + voiceChannelId: channelId, + startTime: dayjs().toISOString(), + discordServerId: guildId, + durationInMinutes: 0, + } as POAPParticipant).catch(Log.error); + if (resultInsert == null || resultInsert.insertedCount !== 1) { + throw new MongoError('failed to insert poapParticipant'); } - await poapParticipantsDb.updateOne(poapParticipant, { - $set: { - endTime: (new Date).toISOString(), - durationInMinutes: durationInMinutes, - }, - }).catch(Log.error); + Log.debug(`${user.tag} | joined, channelId: ${channelId}, guildId: ${guildId}, userId: ${user.id}`); return; } - if (poapParticipant !== null && poapParticipant.discordUserId != null && poapParticipant.discordUserId === member.user.id) { - Log.debug(`${member.user.tag} | rejoined ${channel.name} in ${channel.guild.name}`); - await poapParticipantsDb.updateOne(poapParticipant, { + if (participant.endTime != null) { + const updateResult: UpdateWriteOpResult | void = await poapParticipantsDb.updateOne(participant, { $set: { - startTime: currentDate.toISOString(), + startTime: dayjs().toISOString(), }, $unset: { - endTime: null, + endTime: '', }, }).catch(Log.error); - return; - } - - const currentDateStr = (new Date()).toISOString(); - const result: InsertOneWriteOpResult | void = await poapParticipantsDb.insertOne({ - discordUserId: `${member.user.id}`, - discordUserTag: `${member.user.tag}`, - startTime: currentDateStr, - voiceChannelId: `${channel.id}`, - discordServerId: `${channel.guild.id}`, - durationInMinutes: 0, - }).catch(Log.error); - if (result == null || result.insertedCount !== 1) { - throw new MongoError('failed to insert poapParticipant'); + + if (updateResult == null || updateResult.result.ok != 1) { + Log.error('failed to update rejoined participant in db'); + } + Log.debug(`${user.tag} | rejoined, channelId: ${channelId}, guildId: ${guildId}, userId: ${user.id}`); } - Log.debug(`${member.user.tag} | joined ${channel.name} in ${channel.guild.name}`); }; -const hasUserBeenDeafened = (oldState: VoiceState, newState: VoiceState): boolean => { - return newState.deaf != null && newState.deaf && newState.deaf != oldState.deaf; -}; - -const hasUserEnteredChannel = (oldState: VoiceState, newState: VoiceState): boolean => { - return newState.channelId != null && newState.channelId != oldState.channelId; -}; - -const isStateChangeRelatedToActiveEvent = async (oldState: VoiceState, newState: VoiceState): Promise => { - const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); - const channelIdA = oldState.channelId; - const channelIdB = newState.channelId; - const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); - - if (channelIdA != null && channelIdA != '' && channelIdB != null && channelIdB != '' && channelIdA == channelIdB) { - const activeEvent: POAPSettings | null = await poapSettingsDB.findOne({ - isActive: true, - voiceChannelId: channelIdA.toString(), - discordServerId: oldState.guild.id.toString(), - }); - - if (activeEvent != null) { - Log.debug(`state changed related to active event, userId: ${oldState.id}, channelId: ${channelIdA}`); - return true; - } +export const stopTrackingUserParticipation = async (user: BasicUser, guildId: string, channelId: string | null, participant: POAPParticipant | null): Promise => { + if (!participant) { + participant = await retrieveActiveParticipant(user, channelId, guildId); } - if (channelIdB != null) { - const activeEvent: POAPSettings | null = await poapSettingsDB.findOne({ - isActive: true, - voiceChannelId: channelIdB.toString(), - discordServerId: newState.guild.id.toString(), - }); - - if (activeEvent != null) { - Log.debug(`state changed related to active event, userId: ${newState.id}, channelId: ${channelIdB}`); - return true; - } + if (participant == null) { + throw new MongoError('could not find participant in db when trying to stop tracking'); } - Log.debug('state change not related to event'); - return false; -}; - -const removeDeafenedUser = async (oldState: VoiceState, newState: VoiceState) => { + channelId = channelId as string; + guildId = guildId as string; + + const durationInMinutes: number = calculateDuration(participant.startTime, participant.durationInMinutes); const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); - const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + const poapParticipantsDb: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + const result: UpdateWriteOpResult | void = await poapParticipantsDb.updateOne(participant, { + $set: { + endTime: dayjs().toISOString(), + durationInMinutes: durationInMinutes, + }, + }).catch(Log.error); - if (oldState.channelId) { - const result: DeleteWriteOpResultObject | void = await poapSettingsDB.deleteOne({ - isActive: true, - voiceChannelId: oldState.channelId.toString(), - discordServerId: oldState.guild.id.toString(), - discordUserId: oldState.id.toString(), - }).catch(Log.warn); - if (result != null && result.deletedCount == 1) { - Log.debug(`user removed from db, userId: ${oldState.id} deafened themselves, channelId: ${oldState.channelId}, discordServerId: ${oldState.id}`); - } + if (result == null || result.result.ok != 1) { + throw new MongoError('failed to update present participant in db'); } - - if (newState.channelId) { - const result: DeleteWriteOpResultObject | void = await poapSettingsDB.deleteOne({ - isActive: true, - voiceChannelId: newState.channelId.toString(), - discordServerId: newState.guild.id.toString(), - discordUserId: newState.id.toString(), - }).catch(Log.warn); - if (result != null && result.deletedCount == 1) { - Log.debug(`user removed from db, userId: ${newState.id} deafened themselves, channelId: ${newState.channelId}, discordServerId: ${newState.id}`); - } + Log.debug(`${user.tag} | left, channelId: ${channelId}, guildId: ${guildId}, userId: ${user.id}`); +}; + +const calculateDuration = (startTime: string, currentDuration: number): number => { + const currentDate: Dayjs = dayjs(); + const startTimeDate: Dayjs = dayjs(startTime); + let durationInMinutes: number = currentDuration; + if ((currentDate.unix() - startTimeDate.unix() > 0)) { + durationInMinutes += ((currentDate.unix() - startTimeDate.unix()) / 60); } + return durationInMinutes; }; -// const startTrackingUserParticipation = async (oldState: VoiceState, newState: VoiceState) => { -// -// }; -// -// const stopTrackingUserParticipation = async (oldState: VoiceState, newState: VoiceState) => { -// -// }; -// -// const hasUserTriggeredState = (oldState: VoiceState, newState: VoiceState): boolean => { -// // Check if user entered a channel -// if (newState.channelId != null && newState.channelId != oldState.channelId) { -// // Log.debug('entered a channel'); -// return true; -// } -// -// // Check if user left all channels -// if (newState.channelId == null && newState.channelId != oldState.channelId) { -// // Log.debug('left all channels'); -// return true; -// } -// -// return false; -// // if (oldState.channelId === newState.channelId && (oldState.deaf == newState.deaf)) { -// // // user did not change channels -// // return false; -// // } -// // const member: GuildMember | null = (oldState.guild != null) ? oldState.member : newState.member; -// // -// // if (member == null) { -// // // could not find member -// // return false; -// // } -// // return true; -// }; +const retrieveActiveParticipant = async ( + user: BasicUser, channelId: string | null, guildId: string, +): Promise => { + if (user.id == null || channelId == null || guildId == null) { + return null; + } + const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); + const poapParticipantsDb: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + return await poapParticipantsDb.findOne({ + discordUserId: user.id, + voiceChannelId: channelId, + discordServerId: guildId, + }); +}; export default HandleParticipantDuringEvent; \ No newline at end of file diff --git a/src/app/service/poap/end/EndPOAP.ts b/src/app/service/poap/end/EndPOAP.ts index 7a2f8eed..cfeb1125 100644 --- a/src/app/service/poap/end/EndPOAP.ts +++ b/src/app/service/poap/end/EndPOAP.ts @@ -5,11 +5,20 @@ import { MessageOptions, TextChannel, } from 'discord.js'; -import { Collection, Db, UpdateWriteOpResult } from 'mongodb'; +import { + Collection as MongoCollection, + Collection, + Cursor, + Db, + UpdateWriteOpResult, +} from 'mongodb'; import constants from '../../constants/constants'; import { POAPSettings } from '../../../types/poap/POAPSettings'; import POAPUtils, { POAPFileParticipant } from '../../../utils/POAPUtils'; -import { CommandContext, MessageOptions as MessageOptionsSlash } from 'slash-create'; +import { + CommandContext, + MessageOptions as MessageOptionsSlash, +} from 'slash-create'; import Log from '../../../utils/Log'; import dayjs from 'dayjs'; import MongoDbUtils from '../../../utils/MongoDbUtils'; @@ -17,6 +26,8 @@ import ServiceUtils from '../../../utils/ServiceUtils'; import EndTwitterFlow from './EndTwitterFlow'; import { POAPDistributionResults } from '../../../types/poap/POAPDistributionResults'; import channelIds from '../../constants/channelIds'; +import { POAPParticipant } from '../../../types/poap/POAPParticipant'; +import { stopTrackingUserParticipation } from '../../../events/poap/HandleParticipantDuringEvent'; export default async (guildMember: GuildMember, platform: string, ctx?: CommandContext): Promise => { Log.debug('attempting to end poap event'); @@ -32,11 +43,11 @@ export default async (guildMember: GuildMember, platform: string, ctx?: CommandC } const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); - const poapSettingsDoc: POAPSettings | null = await poapSettingsDB.findOne({ + const poapSettingsDoc: POAPSettings | null | void = await poapSettingsDB.findOne({ discordUserId: guildMember.user.id, discordServerId: guildMember.guild.id, isActive: true, - }); + }).catch(Log.error); if (poapSettingsDoc == null) { Log.debug('poap event not found'); @@ -80,7 +91,7 @@ export default async (guildMember: GuildMember, platform: string, ctx?: CommandC Log.debug(`poap event ended for ${guildMember.user.tag} and updated in db`, { indexMeta: true, meta: { - discordId: poapSettingsDoc.discordServerId, + guildId: poapSettingsDoc.discordServerId, voiceChannelId: poapSettingsDoc.voiceChannelId, event: poapSettingsDoc.event, }, @@ -88,16 +99,16 @@ export default async (guildMember: GuildMember, platform: string, ctx?: CommandC const channel: GuildChannel | null = await guildMember.guild.channels.fetch(poapSettingsDoc.voiceChannelId); if (channel == null) { - Log.warn('channel not found'); - return; + Log.warn('channel not found, might have been deleted, oh well'); } - const listOfParticipants: POAPFileParticipant[] = await POAPUtils.getListOfParticipants(db, channel); + await handleEventEndForPresentParticipants(poapSettingsDoc); + const listOfParticipants: POAPFileParticipant[] = await POAPUtils.getListOfParticipants(poapSettingsDoc); const numberOfParticipants: number = listOfParticipants.length; if (numberOfParticipants <= 0) { Log.debug('no eligible attendees found during event'); - const eventEndMsg = `POAP event ended. No participants found for \`${channel.name}\` in \`${channel.guild.name}\`.`; + const eventEndMsg = `POAP event ended. No participants found for \`${channel?.name}\` in \`${channel?.guild.name}\`.`; if (isDmOn) { await guildMember.send({ content: eventEndMsg }); } else if (ctx) { @@ -118,8 +129,8 @@ export default async (guildMember: GuildMember, platform: string, ctx?: CommandC fields: [ { name: 'Date', value: `${currentDate} UTC`, inline: true }, { name: 'Event', value: `${poapSettingsDoc.event}`, inline: true }, - { name: 'Discord Server', value: channel.guild.name, inline: true }, - { name: 'Location', value: channel.name, inline: true }, + { name: 'Discord Server', value: `${channel?.guild.name} `, inline: true }, + { name: 'Location', value: `${channel?.name} `, inline: true }, { name: 'Total Participants', value: `${numberOfParticipants}`, inline: true }, ], }, @@ -153,3 +164,25 @@ export default async (guildMember: GuildMember, platform: string, ctx?: CommandC await POAPUtils.handleDistributionResults(isDmOn, guildMember, distributionResults, channelExecution, ctx); Log.debug('POAP end complete'); }; + +const handleEventEndForPresentParticipants = async ( + poapSettingsDoc: POAPSettings, +): Promise => { + Log.debug('starting to handle present members for end of poap event'); + const participantsCursor: Cursor = await getPoapParticipantsFromDB(poapSettingsDoc.voiceChannelId, poapSettingsDoc.discordServerId); + for await (const participant of participantsCursor) { + if (participant.endTime == null || participant.endTime == '') { + await stopTrackingUserParticipation({ id: participant.discordUserId, tag: participant.discordUserTag }, participant.discordServerId, participant.voiceChannelId, participant); + } + } + Log.debug('finished setting endDate for present participants in db'); +}; + +export const getPoapParticipantsFromDB = async (channelId: string, guildId: string): Promise> => { + const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); + const poapParticipants: MongoCollection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + return poapParticipants.find({ + voiceChannelId: channelId, + discordServerId: guildId, + }); +}; diff --git a/src/app/service/poap/start/StartPOAP.ts b/src/app/service/poap/start/StartPOAP.ts index e90270f0..e2d72d4e 100644 --- a/src/app/service/poap/start/StartPOAP.ts +++ b/src/app/service/poap/start/StartPOAP.ts @@ -16,7 +16,6 @@ import { } from 'slash-create'; import { Collection, - Cursor, Db, FindAndModifyWriteOpResultObject, } from 'mongodb'; @@ -34,7 +33,10 @@ import MongoDbUtils from '../../../utils/MongoDbUtils'; import StartTwitterFlow from './StartTwitterFlow'; import StartChannelFlow from './StartChannelFlow'; import channelIds from '../../constants/channelIds'; -import { updateUserForPOAP } from '../../../events/poap/HandleParticipantDuringEvent'; +import { + startTrackingUserParticipation, +} from '../../../events/poap/HandleParticipantDuringEvent'; +import { POAPParticipant } from '../../../types/poap/POAPParticipant'; export default async (ctx: CommandContext, guildMember: GuildMember, platform: string, event: string, duration?: number): Promise => { if (ctx.guildID == undefined) { @@ -55,19 +57,19 @@ export default async (ctx: CommandContext, guildMember: GuildMember, platform: s return; } - const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); - const activeSettingsCursor: Cursor = await poapSettingsDB.find({ + const poapSettingsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_SETTINGS); + const activeSettings: POAPSettings | null | void = await poapSettingsDB.findOne({ discordUserId: guildMember.id, discordServerId: guildMember.guild.id, isActive: true, - }); - const activeSettings: POAPSettings | null = await activeSettingsCursor.next(); + }).catch(Log.error); + if (activeSettings != null) { Log.debug('unable to start due to active event'); throw new ValidationError(`Please end \`${activeSettings.voiceChannelName}\` event before starting a new event.`); } - const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Hello! For which voice channel should the POAP event occur?'); + const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Hello! In which voice channel should the POAP event start?'); const voiceChannels: DiscordCollection = ServiceUtils.getAllVoiceChannels(guildMember); if (!isDmOn) { @@ -83,12 +85,12 @@ export default async (ctx: CommandContext, guildMember: GuildMember, platform: s throw new ValidationError('Missing channel'); } - const poapSettingsDoc: POAPSettings = await poapSettingsDB.findOne({ + const poapSettingsDoc: POAPSettings | null | void = await poapSettingsDB.findOne({ discordServerId: channelChoice.guild.id, voiceChannelId: channelChoice.id, - }); + }).catch(Log.error); - if (poapSettingsDoc !== null && poapSettingsDoc.isActive) { + if (poapSettingsDoc != null && poapSettingsDoc.isActive) { Log.info('unable to start due to active event'); await guildMember.send({ content: 'Event is already active.' }); throw new ValidationError(`\`${channelChoice.name}\` is already active. Please reach out to <@${poapSettingsDoc.discordUserId}> to end event.`); @@ -110,13 +112,13 @@ export default async (ctx: CommandContext, guildMember: GuildMember, platform: s ], }, ], - }); - await guildMember.send({ content: 'Everything is set, catch you later!' }); + }).catch(Log.error); + await guildMember.send({ content: 'Everything is set, catch you later!' }).catch(Log.error); }; export const clearPOAPParticipants = async (db: Db, guildChannel: GuildChannel): Promise => { Log.debug(`attempting to delete all previous participants for ${guildChannel.guild.name} on channel: ${guildChannel.name}`); - const poapParticipantsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); + const poapParticipantsDB: Collection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); await poapParticipantsDB.deleteMany({ voiceChannelId: guildChannel.id, discordServerId: guildChannel.guild.id, @@ -133,7 +135,9 @@ export const clearPOAPParticipants = async (db: Db, guildChannel: GuildChannel): export const storePresentMembers = async (db: Db, channel: GuildChannel): Promise => { try { channel.members.forEach((member: GuildMember) => { - updateUserForPOAP(member, db, channel, true); + if (!member.voice.deaf) { + startTrackingUserParticipation({ id: member.id, tag: member.user.tag }, channel.guildId, channel.id).catch(Log.error); + } }); } catch (e) { LogUtils.logError('failed to store present members', e); diff --git a/src/app/types/poap/POAPParticipant.ts b/src/app/types/poap/POAPParticipant.ts index fa2f2bd5..9820f146 100644 --- a/src/app/types/poap/POAPParticipant.ts +++ b/src/app/types/poap/POAPParticipant.ts @@ -6,7 +6,7 @@ export interface POAPParticipant extends Collection { discordUserId: string, discordUserTag: string, startTime: string, - endTime: string, + endTime: string | null, voiceChannelId: string, discordServerId: string, durationInMinutes: number, diff --git a/src/app/utils/POAPUtils.ts b/src/app/utils/POAPUtils.ts index 0a1b4712..a67d6801 100644 --- a/src/app/utils/POAPUtils.ts +++ b/src/app/utils/POAPUtils.ts @@ -1,7 +1,6 @@ import { AwaitMessagesOptions, DMChannel, - GuildChannel, GuildMember, Message, MessageActionRow, @@ -32,6 +31,8 @@ import { POAPDistributionResults } from '../types/poap/POAPDistributionResults'; import ApiKeys from '../service/constants/apiKeys'; import buttonIds from '../service/constants/buttonIds'; import { DiscordUserCollection } from '../types/discord/DiscordUserCollection'; +import { POAPSettings } from '../types/poap/POAPSettings'; +import { getPoapParticipantsFromDB } from '../service/poap/end/EndPOAP'; export type POAPFileParticipant = { discordUserId: string, @@ -49,22 +50,17 @@ export type TwitterPOAPFileParticipant = { const POAPUtils = { - async getListOfParticipants(db: Db, voiceChannel: GuildChannel): Promise { - const poapParticipants: MongoCollection = db.collection(constants.DB_COLLECTION_POAP_PARTICIPANTS); - const resultCursor: Cursor = await poapParticipants.find({ - voiceChannelId: voiceChannel.id, - discordServerId: voiceChannel.guild.id, - }); - - if ((await resultCursor.count()) === 0) { - Log.debug(`no participants found for ${voiceChannel.name} in ${voiceChannel.guild.name}`); + async getListOfParticipants(poapSettingsDoc: POAPSettings): Promise { + Log.debug('checking for participants in db cursor'); + const participantsCursor: Cursor = await getPoapParticipantsFromDB(poapSettingsDoc.voiceChannelId, poapSettingsDoc.discordServerId); + if ((await participantsCursor.count()) === 0) { + Log.debug('no participants found'); return []; } - - await POAPUtils.setEndDateForPresentParticipants(poapParticipants, resultCursor); + Log.debug('found participants from cursor'); const participants: POAPFileParticipant[] = []; - await resultCursor.forEach((participant: POAPParticipant) => { + await participantsCursor.forEach((participant: POAPParticipant) => { if (participant.durationInMinutes >= constants.POAP_REQUIRED_PARTICIPATION_DURATION) { participants.push({ discordUserId: participant.discordUserId, @@ -73,6 +69,7 @@ const POAPUtils = { }); } }); + Log.debug('finished preparing participants array'); return participants; }, @@ -98,38 +95,6 @@ const POAPUtils = { return participants; }, - async setEndDateForPresentParticipants(poapParticipantsCollection: MongoCollection, poapParticipantsCursor: Cursor): Promise { - Log.debug('starting to set endDate for present participants in db'); - const currentDateStr = dayjs().toISOString(); - for await (const participant of poapParticipantsCursor) { - if (participant.endTime != null) { - // skip setting endDate for present endTime; - continue; - } - let result: UpdateWriteOpResult; - try { - const currentDate: Dayjs = dayjs(); - const startTimeDate: Dayjs = dayjs(participant.startTime); - let durationInMinutes: number = participant.durationInMinutes; - if ((currentDate.unix() - startTimeDate.unix() > 0)) { - durationInMinutes += ((currentDate.unix() - startTimeDate.unix()) / 60); - } - result = await poapParticipantsCollection.updateOne(participant, { - $set: { - endTime: currentDateStr, - durationInMinutes: durationInMinutes, - }, - }); - if (result == null) { - throw new Error('Mongodb operation failed'); - } - } catch (e) { - LogUtils.logError('failed to update poap participants with endTime', e); - } - } - Log.debug('finished setting endDate for present participants in db'); - }, - async askForPOAPLinks( guildMember: GuildMember, isDmOn: boolean, numberOfParticipants: number, ctx?: CommandContext, adminChannel?: TextChannel | null, From 77fb1617e05d77e787bf59105b2dd2de34e2ffef Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 10 Jan 2022 11:04:41 -0500 Subject: [PATCH 12/20] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e097c12..18ef3324 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - fix poap mint api call - fix auto end for DM event - fix gm regex for single line + - refactor poap start/stop tracking event ## 2.5.2-RELEASE From daeda4f7b0cf2a483d6d7493dc5dcf7b8a5e4cc9 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 10 Jan 2022 14:52:48 -0500 Subject: [PATCH 13/20] adjust print --- CHANGELOG.md | 1 + src/app/utils/POAPUtils.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18ef3324..92a208a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - fix auto end for DM event - fix gm regex for single line - refactor poap start/stop tracking event +2. Twitter stability check ## 2.5.2-RELEASE diff --git a/src/app/utils/POAPUtils.ts b/src/app/utils/POAPUtils.ts index a67d6801..ef691d35 100644 --- a/src/app/utils/POAPUtils.ts +++ b/src/app/utils/POAPUtils.ts @@ -434,7 +434,7 @@ const POAPUtils = { guildMember: GuildMember, distributionResults: POAPDistributionResults, event: string, platform: string, ): Promise { - Log.debug(`${distributionResults.didNotSendList} poaps were not sent`); + Log.debug(`${distributionResults.didNotSendList.length} poaps were not sent`); const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); if (platform == constants.PLATFORM_TYPE_DISCORD) { From b89a85494a5f5a6b905fdc469929160db61d756a Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 10 Jan 2022 15:18:55 -0500 Subject: [PATCH 14/20] async ask for input --- src/app/service/poap/POAPService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/service/poap/POAPService.ts b/src/app/service/poap/POAPService.ts index 8bd98bf8..20257f51 100644 --- a/src/app/service/poap/POAPService.ts +++ b/src/app/service/poap/POAPService.ts @@ -46,9 +46,9 @@ const POAPService = { for (const expiredEvent of expiredEventsList) { const poapGuild: Guild = await client.guilds.fetch(expiredEvent.discordServerId); const poapOrganizer: GuildMember = await poapGuild.members.fetch(expiredEvent.discordUserId); - await EndPOAP(poapOrganizer, platform).catch(Log.error); + EndPOAP(poapOrganizer, platform).catch(Log.error); } - Log.debug(`all expired events ended for ${platform}, now checking for active events`); + Log.debug(`all expired events ended for ${platform} and possibly pending user intpu, now checking for active events`); const poapSettingsActiveEventsCursor: Cursor = await poapSettingsDB.find({ isActive: true, endTime: { @@ -71,7 +71,7 @@ const POAPService = { if (!channelChoice) { throw new ValidationError('Missing channel'); } - await storePresentMembers(db, channelChoice).catch(Log.error); + storePresentMembers(db, channelChoice).catch(Log.error); } catch (e) { LogUtils.logError('failed trying to store present members for active poap event', e); } From ef292f46825bb33d5cdeb0c9b0d76ad5c7cae8c0 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 10 Jan 2022 15:33:18 -0500 Subject: [PATCH 15/20] handle timeout for autoend situation --- CHANGELOG.md | 1 + src/app/service/poap/POAPService.ts | 7 ++++++- src/app/utils/POAPUtils.ts | 16 +++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92a208a9..29dc59c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - fix gm regex for single line - refactor poap start/stop tracking event 2. Twitter stability check + - handle timeout for autoend situation ## 2.5.2-RELEASE diff --git a/src/app/service/poap/POAPService.ts b/src/app/service/poap/POAPService.ts index 20257f51..2e0ffc1c 100644 --- a/src/app/service/poap/POAPService.ts +++ b/src/app/service/poap/POAPService.ts @@ -46,7 +46,12 @@ const POAPService = { for (const expiredEvent of expiredEventsList) { const poapGuild: Guild = await client.guilds.fetch(expiredEvent.discordServerId); const poapOrganizer: GuildMember = await poapGuild.members.fetch(expiredEvent.discordUserId); - EndPOAP(poapOrganizer, platform).catch(Log.error); + EndPOAP(poapOrganizer, platform).catch((e) => { + if (e instanceof ValidationError) { + poapOrganizer.send({ content: `${e?.message}` }).catch(Log.error); + } + Log.error(e); + }); } Log.debug(`all expired events ended for ${platform} and possibly pending user intpu, now checking for active events`); const poapSettingsActiveEventsCursor: Cursor = await poapSettingsDB.find({ diff --git a/src/app/utils/POAPUtils.ts b/src/app/utils/POAPUtils.ts index ef691d35..d67fdca6 100644 --- a/src/app/utils/POAPUtils.ts +++ b/src/app/utils/POAPUtils.ts @@ -112,24 +112,30 @@ const POAPUtils = { if (isDmOn) { await guildMember.send({ content: uploadLinksMsg }); const dmChannel: DMChannel = await guildMember.createDM(); - message = (await dmChannel.awaitMessages(replyOptions)).first(); + message = (await dmChannel.awaitMessages(replyOptions).catch(() => { + throw new ValidationError('Invalid attachment. Session ended, please try the command again.'); + })).first(); } else if (ctx) { await ctx.sendFollowUp(uploadLinksMsg); const guildChannel: TextChannel = await guildMember.guild.channels.fetch(ctx.channelID) as TextChannel; - message = (await guildChannel.awaitMessages(replyOptions)).first(); + message = (await guildChannel.awaitMessages(replyOptions).catch(() => { + throw new ValidationError('Invalid attachment. Session ended, please try the command again.'); + })).first(); } else if (adminChannel != null) { await adminChannel.send(uploadLinksMsg); - message = (await adminChannel.awaitMessages(replyOptions)).first(); + message = (await adminChannel.awaitMessages(replyOptions).catch(() => { + throw new ValidationError('Invalid attachment. Session ended, please try the command again.'); + })).first(); } if (message == null) { - throw new ValidationError('Invalid attachment. Session ended. Please try the command again.'); + throw new ValidationError('Invalid attachment. Session ended, please try the command again.'); } const poapLinksFile: MessageAttachment | undefined = message.attachments.first(); if (poapLinksFile == null) { - throw new ValidationError('Invalid attachment. Session ended. Please try the command again.'); + throw new ValidationError('Invalid attachment. Session ended, please try the command again.'); } Log.debug(`obtained poap links attachment in discord: ${poapLinksFile.url}`); From 378e6f57635608b400389a5e6984027a0749ea78 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 10 Jan 2022 16:32:35 -0500 Subject: [PATCH 16/20] handle autoend for twitter spaces --- package.json | 1 + src/app/service/poap/end/EndTwitterFlow.ts | 6 +- .../service/poap/start/StartTwitterFlow.ts | 5 +- src/app/utils/POAPUtils.ts | 62 +++++++++++-------- 4 files changed, 45 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index a1168054..e14d9bd7 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "prestart": "yarn install && yarn build && yarn lint", "pretest": "yarn install && yarn build", "start": "node --trace-warnings -r dotenv/config dist/app/app.js", + "force-start": "tsc -p tsconfig.json && node -r dotenv/config dist/app/app.js", "lint": "eslint . --ext .ts", "format": "eslint . --ext .ts --fix", "watch": "tsc -p tsconfig.json -w" diff --git a/src/app/service/poap/end/EndTwitterFlow.ts b/src/app/service/poap/end/EndTwitterFlow.ts index ed7f9a3e..506358fb 100644 --- a/src/app/service/poap/end/EndTwitterFlow.ts +++ b/src/app/service/poap/end/EndTwitterFlow.ts @@ -36,7 +36,7 @@ const EndTwitterFlow = async (guildMember: GuildMember, db: Db, ctx?: CommandCon } Log.debug('active twitter poap event found'); - const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Over already? Can\'t wait for the next one'); + const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Hello! I found a poap event, let me try ending it.'); let channelExecution: TextChannel | null = null; if (!isDmOn && ctx) { @@ -103,7 +103,9 @@ const EndTwitterFlow = async (guildMember: GuildMember, db: Db, ctx?: CommandCon const poapLinksFile: MessageAttachment = await POAPUtils.askForPOAPLinks(guildMember, isDmOn, numberOfParticipants, ctx); const listOfPOAPLinks: string[] = await POAPUtils.getListOfPoapLinks(poapLinksFile); const distributionResults: POAPDistributionResults = await POAPUtils.sendOutTwitterPoapLinks(listOfParticipants, activeTwitterSettings.event, listOfPOAPLinks); - await POAPUtils.setupFailedAttendeesDelivery(guildMember, distributionResults, activeTwitterSettings.event, constants.PLATFORM_TYPE_TWITTER); + if (distributionResults.didNotSendList.length > 0) { + await POAPUtils.setupFailedAttendeesDelivery(guildMember, distributionResults, activeTwitterSettings.event, constants.PLATFORM_TYPE_TWITTER); + } await POAPUtils.handleDistributionResults(isDmOn, guildMember, distributionResults, channelExecution, ctx); Log.debug('POAP twitter end complete'); }; diff --git a/src/app/service/poap/start/StartTwitterFlow.ts b/src/app/service/poap/start/StartTwitterFlow.ts index dee73eb6..de87eed1 100644 --- a/src/app/service/poap/start/StartTwitterFlow.ts +++ b/src/app/service/poap/start/StartTwitterFlow.ts @@ -12,6 +12,7 @@ import ValidationError from '../../../errors/ValidationError'; import dayjs, { Dayjs } from 'dayjs'; import POAPService from '../POAPService'; import { POAPTwitterParticipants } from '../../../types/poap/POAPTwitterParticipants'; +import channelIds from '../../constants/channelIds'; const StartTwitterFlow = async (ctx: CommandContext, guildMember: GuildMember, db: Db, event: string, duration: number): Promise => { Log.debug('starting twitter poap flow...'); @@ -65,6 +66,7 @@ const StartTwitterFlow = async (ctx: CommandContext, guildMember: GuildMember, d Log.debug('setting up active twitter event in db'); const currentDate: Dayjs = dayjs(); const endTimeISO: string = currentDate.add(duration, 'minute').toISOString(); + const channelExecutionId: string = isDmOn ? channelIds.DM : ctx.channelID; const twitterSettingsResult: FindAndModifyWriteOpResultObject = await poapTwitterSettings.findOneAndReplace({ discordUserId: guildMember.id, discordServerId: guildMember.guild.id, @@ -78,7 +80,8 @@ const StartTwitterFlow = async (ctx: CommandContext, guildMember: GuildMember, d discordServerId: guildMember.guild.id, twitterUserId: verifiedTwitter.twitterUser.id_str, twitterSpaceId: twitterSpaceId, - }, { + channelExecutionId: channelExecutionId, + } as POAPTwitterSettings, { upsert: true, returnDocument: 'after', }); diff --git a/src/app/utils/POAPUtils.ts b/src/app/utils/POAPUtils.ts index d67fdca6..6e401760 100644 --- a/src/app/utils/POAPUtils.ts +++ b/src/app/utils/POAPUtils.ts @@ -33,6 +33,7 @@ import buttonIds from '../service/constants/buttonIds'; import { DiscordUserCollection } from '../types/discord/DiscordUserCollection'; import { POAPSettings } from '../types/poap/POAPSettings'; import { getPoapParticipantsFromDB } from '../service/poap/end/EndPOAP'; +import { POAPTwitterUnclaimedParticipants } from '../types/poap/POAPTwitterUnclaimedParticipants'; export type POAPFileParticipant = { discordUserId: string, @@ -154,7 +155,7 @@ const POAPUtils = { } catch (e) { listOfPOAPLinks = []; } - Log.debug(`DEGEN given ${listOfPOAPLinks.length} poap links`); + Log.debug(`${constants.APP_NAME} given ${listOfPOAPLinks.length} poap links`); return listOfPOAPLinks; } catch (e) { LogUtils.logError('failed to process links.txt file', e); @@ -369,12 +370,12 @@ const POAPUtils = { didNotSendList: [], }; - const twitterClient: TwitterApi = new TwitterApi({ - appKey: apiKeys.twitterAppToken, - appSecret: apiKeys.twitterAppSecret, - accessToken: apiKeys.twitterAccessToken, - accessSecret: apiKeys.twitterSecretToken, - } as TwitterApiTokens); + // const twitterClient: TwitterApi = new TwitterApi({ + // appKey: apiKeys.twitterAppToken, + // appSecret: apiKeys.twitterAppSecret, + // accessToken: apiKeys.twitterAccessToken, + // accessSecret: apiKeys.twitterSecretToken, + // } as TwitterApiTokens); while (i < length) { const participant: TwitterPOAPFileParticipant | undefined = listOfParticipants.pop(); if (participant == null) { @@ -401,23 +402,24 @@ const POAPUtils = { continue; } try { - const result: void | DirectMessageCreateV1Result = await twitterClient.v1.sendDm({ - recipient_id: participant.twitterUserId, - text: `Thank you for participating in ${event}. Here is your POAP: ${poapLink} Enjoy! (gm)`, - quick_reply: { - type: 'options', - options: [ - { - label: 'gm', - description: 'Good Morning', - metadata: 'good_morning', - }, - ], - }, - }); - if (result == null || result['event'].type != 'message_create') { - throw new Error(); - } + throw new Error(); + // const result: void | DirectMessageCreateV1Result = await twitterClient.v1.sendDm({ + // recipient_id: participant.twitterUserId, + // text: `Thank you for participating in ${event}. Here is your POAP: ${poapLink} Enjoy! (gm)`, + // quick_reply: { + // type: 'options', + // options: [ + // { + // label: 'gm', + // description: 'Good Morning', + // metadata: 'good_morning', + // }, + // ], + // }, + // }); + // if (result == null || result['event'].type != 'message_create') { + // throw new Error(); + // } results.successfullySent++; } catch (e) { LogUtils.logError(`user might have been banned or has DMs off, failed trying to send POAP to twitterId: ${participant.twitterUserId}, twitterSpaceId: ${participant.twitterSpaceId}, link: ${poapLink}`, e); @@ -440,6 +442,11 @@ const POAPUtils = { guildMember: GuildMember, distributionResults: POAPDistributionResults, event: string, platform: string, ): Promise { + if (distributionResults.didNotSendList.length <= 0) { + Log.warn('failed delivery participants not found'); + return; + } + Log.debug(`${distributionResults.didNotSendList.length} poaps were not sent`); const db: Db = await MongoDbUtils.connect(constants.DB_NAME_DEGEN); @@ -471,10 +478,13 @@ const POAPUtils = { expiresAt: expirationISO, twitterUserId: failedAttendee.twitterUserId, twitterSpaceId: failedAttendee.twitterSpaceId, - }; + } as POAPTwitterUnclaimedParticipants; }); Log.debug('attempting to store failed attendees into db'); - await unclaimedCollection.insertMany(unclaimedPOAPsList); + await unclaimedCollection.insertMany(unclaimedPOAPsList).catch(e => { + Log.error(e); + throw new ValidationError('failed trying to store unclaimed participants, please try distribution command'); + }); distributionResults.claimSetUp = unclaimedPOAPsList.length; } else { Log.warn('missing platform type when trying to setup failed attendees'); From 8c23a1623fe848003097a848333c0149d7b10cf5 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 10 Jan 2022 16:34:20 -0500 Subject: [PATCH 17/20] update changelog --- CHANGELOG.md | 3 +++ src/app/utils/POAPUtils.ts | 47 +++++++++++++++++++------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29dc59c4..0f7c9244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ - refactor poap start/stop tracking event 2. Twitter stability check - handle timeout for autoend situation + - better error messaging + - stability enhancements + - add forced start script ## 2.5.2-RELEASE diff --git a/src/app/utils/POAPUtils.ts b/src/app/utils/POAPUtils.ts index 6e401760..fb317d85 100644 --- a/src/app/utils/POAPUtils.ts +++ b/src/app/utils/POAPUtils.ts @@ -370,12 +370,12 @@ const POAPUtils = { didNotSendList: [], }; - // const twitterClient: TwitterApi = new TwitterApi({ - // appKey: apiKeys.twitterAppToken, - // appSecret: apiKeys.twitterAppSecret, - // accessToken: apiKeys.twitterAccessToken, - // accessSecret: apiKeys.twitterSecretToken, - // } as TwitterApiTokens); + const twitterClient: TwitterApi = new TwitterApi({ + appKey: apiKeys.twitterAppToken, + appSecret: apiKeys.twitterAppSecret, + accessToken: apiKeys.twitterAccessToken, + accessSecret: apiKeys.twitterSecretToken, + } as TwitterApiTokens); while (i < length) { const participant: TwitterPOAPFileParticipant | undefined = listOfParticipants.pop(); if (participant == null) { @@ -402,24 +402,23 @@ const POAPUtils = { continue; } try { - throw new Error(); - // const result: void | DirectMessageCreateV1Result = await twitterClient.v1.sendDm({ - // recipient_id: participant.twitterUserId, - // text: `Thank you for participating in ${event}. Here is your POAP: ${poapLink} Enjoy! (gm)`, - // quick_reply: { - // type: 'options', - // options: [ - // { - // label: 'gm', - // description: 'Good Morning', - // metadata: 'good_morning', - // }, - // ], - // }, - // }); - // if (result == null || result['event'].type != 'message_create') { - // throw new Error(); - // } + const result: void | DirectMessageCreateV1Result = await twitterClient.v1.sendDm({ + recipient_id: participant.twitterUserId, + text: `Thank you for participating in ${event}. Here is your POAP: ${poapLink} Enjoy! (gm)`, + quick_reply: { + type: 'options', + options: [ + { + label: 'gm', + description: 'Good Morning', + metadata: 'good_morning', + }, + ], + }, + }); + if (result == null || result['event'].type != 'message_create') { + throw new Error(); + } results.successfullySent++; } catch (e) { LogUtils.logError(`user might have been banned or has DMs off, failed trying to send POAP to twitterId: ${participant.twitterUserId}, twitterSpaceId: ${participant.twitterSpaceId}, link: ${poapLink}`, e); From 5eaeb4b9f53bcf62212e6fcb1cc7435bb8f9ea29 Mon Sep 17 00:00:00 2001 From: Brian Date: Mon, 10 Jan 2022 20:48:13 -0500 Subject: [PATCH 18/20] input, not intpu --- src/app/service/poap/POAPService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/service/poap/POAPService.ts b/src/app/service/poap/POAPService.ts index 2e0ffc1c..084907b9 100644 --- a/src/app/service/poap/POAPService.ts +++ b/src/app/service/poap/POAPService.ts @@ -53,7 +53,7 @@ const POAPService = { Log.error(e); }); } - Log.debug(`all expired events ended for ${platform} and possibly pending user intpu, now checking for active events`); + Log.debug(`all expired events ended for ${platform} and possibly pending user input, now checking for active events`); const poapSettingsActiveEventsCursor: Cursor = await poapSettingsDB.find({ isActive: true, endTime: { From b297e5832acb55a2a793710bc1e31144bc9f5d22 Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 11 Jan 2022 13:49:39 -0500 Subject: [PATCH 19/20] quick fix enhancements for serendipity --- .github/workflows/deploy-qa.yml | 4 ++-- CHANGELOG.md | 3 +++ src/app/commands/admin/Account.ts | 2 +- src/app/service/account/VerifyTwitter.ts | 15 +++++++++------ src/app/service/poap/ClaimPOAP.ts | 2 +- src/app/service/poap/start/StartTwitterFlow.ts | 4 +++- src/app/utils/ServiceUtils.ts | 1 - 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 54f1e3ca..e4decf5f 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -44,8 +44,8 @@ jobs: HD_LOGDNA_TOKEN: ${{secrets.QA_LOGDNA_TOKEN}} HD_TWITTER_API_TOKEN: ${{secrets.QA_TWITTER_API_TOKEN}} HD_TWITTER_API_SECRET: ${{secrets.QA_TWITTER_API_SECRET}} - HD_TWITTER_BEARER_TOKEN: ${{secrets.QA_TWITTER_API_BEARER_TOKEN}} - HD_TWITTER_ACCESS_TOKEN_SECRET: ${{secrets.QA_TWITTER_API_ACCESS_TOKEN_SECRET}} + HD_TWITTER_BEARER_TOKEN: ${{secrets.QA_TWITTER_BEARER_TOKEN}} + HD_TWITTER_ACCESS_TOKEN_SECRET: ${{secrets.QA_TWITTER_ACCESS_TOKEN_SECRET}} HD_SENTRY_IO_DSN: ${{secrets.QA_SENTRY_IO_DSN}} - name: Get current package version id: package-version diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f7c9244..2964bf2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ - better error messaging - stability enhancements - add forced start script +3. Fix key github action reference for twitter spaces + - remove extra logging + - send twitter auth confirmation only on direct auth flow ## 2.5.2-RELEASE diff --git a/src/app/commands/admin/Account.ts b/src/app/commands/admin/Account.ts index 4f18daa8..304b78e5 100644 --- a/src/app/commands/admin/Account.ts +++ b/src/app/commands/admin/Account.ts @@ -53,7 +53,7 @@ export default class Account extends SlashCommand { const { guildMember } = await ServiceUtils.getGuildAndMember(ctx.guildID, ctx.user.id); try { - await VerifyTwitter(ctx, guildMember).catch(e => { throw e; }); + await VerifyTwitter(ctx, guildMember, true).catch(e => { throw e; }); } catch (e) { if (e instanceof ValidationError) { await ctx.send({ content: `${e.message}`, ephemeral: true }); diff --git a/src/app/service/account/VerifyTwitter.ts b/src/app/service/account/VerifyTwitter.ts index 52acf6c2..a3c1ef11 100644 --- a/src/app/service/account/VerifyTwitter.ts +++ b/src/app/service/account/VerifyTwitter.ts @@ -21,10 +21,10 @@ export type VerifiedTwitter = { twitterClientV1: TwitterApi }; -const VerifyTwitter = async (ctx: CommandContext, guildMember: GuildMember): Promise => { +const VerifyTwitter = async (ctx: CommandContext, guildMember: GuildMember, sendConfirmationMsg: boolean): Promise => { Log.debug('starting to verify twitter account link'); - const isDmOn: boolean = await ServiceUtils.tryDMUser(guildMember, 'Hi! Let me check your twitter info'); + const isDmOn: boolean = (sendConfirmationMsg) ? await ServiceUtils.tryDMUser(guildMember, 'Hi! Let me check your twitter info') : false; if (isDmOn) { await ctx.send({ content: 'DM sent!', ephemeral: true }); @@ -97,10 +97,13 @@ const VerifyTwitter = async (ctx: CommandContext, guildMember: GuildMember): Pro { name: 'URL', value: `https://twitter.com/${userCall.screen_name}` }, ], }; - if (isDmOn) { - await guildMember.send({ embeds: [verifiedEmbeds] }); - } else { - await ctx.send({ embeds: [verifiedEmbeds], ephemeral: true }); + + if (sendConfirmationMsg) { + if (isDmOn) { + await guildMember.send({ embeds: [verifiedEmbeds] }); + } else { + await ctx.send({ embeds: [verifiedEmbeds], ephemeral: true }); + } } Log.debug('done verifying twitter account'); diff --git a/src/app/service/poap/ClaimPOAP.ts b/src/app/service/poap/ClaimPOAP.ts index 1df79f12..cc65e797 100644 --- a/src/app/service/poap/ClaimPOAP.ts +++ b/src/app/service/poap/ClaimPOAP.ts @@ -96,7 +96,7 @@ export const claimForDiscord = async (userId: string, ctx?: CommandContext | nul const claimPOAPForTwitter = async (ctx: CommandContext, guildMember: GuildMember) => { Log.debug('claiming POAP for Twitter'); - const verifiedTwitter: VerifiedTwitter | undefined = await VerifyTwitter(ctx, guildMember); + const verifiedTwitter: VerifiedTwitter | undefined = await VerifyTwitter(ctx, guildMember, false); if (verifiedTwitter == null) { return; } diff --git a/src/app/service/poap/start/StartTwitterFlow.ts b/src/app/service/poap/start/StartTwitterFlow.ts index de87eed1..f36bef87 100644 --- a/src/app/service/poap/start/StartTwitterFlow.ts +++ b/src/app/service/poap/start/StartTwitterFlow.ts @@ -17,7 +17,7 @@ import channelIds from '../../constants/channelIds'; const StartTwitterFlow = async (ctx: CommandContext, guildMember: GuildMember, db: Db, event: string, duration: number): Promise => { Log.debug('starting twitter poap flow...'); - const verifiedTwitter: VerifiedTwitter | undefined = await VerifyTwitter(ctx, guildMember); + const verifiedTwitter: VerifiedTwitter | undefined = await VerifyTwitter(ctx, guildMember, false); if (verifiedTwitter == null) { return; } @@ -26,7 +26,9 @@ const StartTwitterFlow = async (ctx: CommandContext, guildMember: GuildMember, d let twitterSpaceResult: SpaceV2LookupResult | null = null; try { + Log.debug(`twitterId: ${verifiedTwitter.twitterUser.id_str}`); twitterSpaceResult = await twitterClientV2.v2.spacesByCreators(verifiedTwitter.twitterUser.id_str); + Log.debug(twitterSpaceResult.data); } catch (e) { LogUtils.logError('failed trying to get twitter spaces', e); } diff --git a/src/app/utils/ServiceUtils.ts b/src/app/utils/ServiceUtils.ts index 3c992981..5177401a 100644 --- a/src/app/utils/ServiceUtils.ts +++ b/src/app/utils/ServiceUtils.ts @@ -129,7 +129,6 @@ const ServiceUtils = { async tryDMUser(guildMember: GuildMember, message: string): Promise { try { await guildMember.send({ content: message }); - Log.debug(`DM is turned off for ${guildMember.user.tag}`); return true; } catch (e) { LogUtils.logError(`DM is turned off for ${guildMember.user.tag}`, e); From fb12593fb10fc4a5021294779f709b24c068623e Mon Sep 17 00:00:00 2001 From: Brian Date: Tue, 11 Jan 2022 15:04:44 -0500 Subject: [PATCH 20/20] release prep --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2964bf2c..14151e57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2.6.0-SNAPSHOT +## 2.6.0-RELEASE (2022-01-11) 1. Stability check - add sentry github action @@ -17,7 +17,7 @@ - remove extra logging - send twitter auth confirmation only on direct auth flow -## 2.5.2-RELEASE +## 2.5.2-RELEASE (2022-01-09) 1. Address sentry.io issues 2022-01-08 - add type guards to messageCreate sentry method