Skip to content

Commit

Permalink
Merge pull request #2 from BanklessDAO/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
thedarkrai07 authored Jul 22, 2022
2 parents 67903a5 + f5ad003 commit 033bcd7
Show file tree
Hide file tree
Showing 39 changed files with 669 additions and 385 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"dotenv": "^10.0.0",
"mongodb": "^3.6.9",
"nodemon": "^2.0.15",
"slash-create": "^4.4.0",
"slash-create": "^5.6.1",
"typescript": "^4.5.4"
},
"devDependencies": {
Expand Down
14 changes: 2 additions & 12 deletions src/app/activity/bounty/ActivityHandler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { CommandContext } from 'slash-create'
import { createBounty } from './Create'
import { publishBounty } from './Publish'
import { applyBounty } from './Apply'
Expand All @@ -14,8 +13,6 @@ import { upsertUserWallet } from '../user/RegisterWallet';
import { tagBounty } from './Tag';


import { Guild, GuildMember } from 'discord.js';
import client from '../../app';
import Log from '../../utils/Log';

import { CreateRequest } from '../../requests/CreateRequest'
Expand Down Expand Up @@ -48,6 +45,8 @@ export const BountyActivityHandler = {

Log.debug('Reached Activity Handler')
Log.debug(request.activity)


// TODO in all activities, replace any use of request.commandContext with cherry picked fields
// from the commandContext object as top level fields of the [Activity]Request class
switch (request.activity) {
Expand Down Expand Up @@ -99,13 +98,4 @@ export const BountyActivityHandler = {
}
return;
},


async getGuildAndMember(ctx: CommandContext): Promise<{ guild: Guild, guildMember: GuildMember }> {
const guild = await client.guilds.fetch(ctx.guildID);
return {
guild: guild,
guildMember: await guild.members.fetch(ctx.user.id),
};
}
}
13 changes: 10 additions & 3 deletions src/app/activity/bounty/Apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import mongo, { Db, UpdateWriteOpResult } from 'mongodb';
import MongoDbUtils from '../../utils/MongoDbUtils';
import { CustomerCollection } from '../../types/bounty/CustomerCollection';
import BountyUtils from '../../utils/BountyUtils';
import DMPermissionError from '../../errors/DMPermissionError';

export const applyBounty = async (request: ApplyRequest): Promise<any> => {
Log.debug('In Apply activity');
Expand All @@ -17,7 +18,7 @@ export const applyBounty = async (request: ApplyRequest): Promise<any> => {
const pitchMessageText = `Hello @${applyingUser.displayName}!\n` +
`Please respond to the following within 5 minutes.\n` +
`Please tell the bounty creator why you should be chosen to claim this bounty (your pitch)`;
const pitchMessage: Message = await applyingUser.send({ content: pitchMessageText });
const pitchMessage: Message = await applyingUser.send({ content: pitchMessageText }).catch(() => { throw new DMPermissionError(pitchMessageText) });
const dmChannel: DMChannel = await pitchMessage.channel.fetch() as DMChannel;
const replyOptions: AwaitMessagesOptions = {
max: 1,
Expand All @@ -26,6 +27,9 @@ export const applyBounty = async (request: ApplyRequest): Promise<any> => {
errors: ['time'],
};

const gotoDMMessage = 'Go to your DMs to finish appplying for the bounty...';
await DiscordUtils.activityResponse(request.commandContext, request.buttonInteraction, gotoDMMessage);

const pitch = await DiscordUtils.awaitUserDM(dmChannel, replyOptions);
try {
BountyUtils.validatePitch(pitch);
Expand All @@ -40,14 +44,16 @@ export const applyBounty = async (request: ApplyRequest): Promise<any> => {
const appliedForBounty = await writeDbHandler(request, getDbResult.dbBountyResult, applyingUser, pitch);

const cardMessage = await BountyUtils.canonicalCard(appliedForBounty._id, request.activity);

const createdByUser: GuildMember = await applyingUser.guild.members.fetch(appliedForBounty.createdBy.discordId);
let creatorDM = `Your bounty has been applied for by <@${applyingUser.id}> <${cardMessage.url}> \n` +
`Their pitch: ${pitch ? pitch : '<none given>'} \n` +
'Use the "/bounty assign" command to select an applicant who can claim.';

await DiscordUtils.activityNotification(creatorDM, createdByUser);
await DiscordUtils.activityResponse(request.commandContext, `<@${applyingUser.user.id}>, You have applied for this bounty! Reach out to <@${createdByUser.id}> with any questions: ${cardMessage.url}`, applyingUser);
const activityMessage = `<@${applyingUser.user.id}>, You have applied for this bounty! Reach out to <@${createdByUser.id}> with any questions: ${cardMessage.url}`;
await DiscordUtils.activityResponse(request.commandContext, request.buttonInteraction, activityMessage);
await applyingUser.send({ content: activityMessage })
return;
};

Expand Down Expand Up @@ -87,6 +93,7 @@ const writeDbHandler = async (request: ApplyRequest, appliedForBounty: BountyCol
applicants: {
discordId: applyingUser.user.id,
discordHandle: applyingUser.user.tag,
iconUrl: applyingUser.user.avatarURL(),
pitch: pitch,
},
},
Expand Down
21 changes: 12 additions & 9 deletions src/app/activity/bounty/Assign.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GuildMember, Message, MessageEmbed, TextChannel } from 'discord.js';
import { GuildMember, Message, MessageActionRow, MessageButton, MessageEmbed, TextChannel } from 'discord.js';
import { AssignRequest } from '../../requests/AssignRequest';
import { BountyCollection } from '../../types/bounty/BountyCollection';
import { Bounty } from '../../types/bounty/Bounty';
Expand Down Expand Up @@ -33,13 +33,13 @@ export const assignBounty = async (request: AssignRequest): Promise<any> => {
}

const cardMessage = await BountyUtils.canonicalCard(getDbResult.dbBountyResult._id, request.activity);

let assigningContent = `Your bounty has been assigned to <@${assignedUser.user.id}> ${cardMessage.url}`;
let assignedContent = `You have been assigned this bounty! Go to the bounty card to claim it. Reach out to <@${assigningUser.id}> with any questions\n` +
`<${cardMessage.url}>`;

await DiscordUtils.activityNotification(assignedContent, assignedUser);
await DiscordUtils.activityResponse(request.commandContext, assigningContent, assigningUser);
await DiscordUtils.activityResponse(request.commandContext, request.buttonInteraction, assigningContent);
return;
};

Expand Down Expand Up @@ -75,8 +75,11 @@ const writeDbHandler = async (request: AssignRequest, assignedBounty: BountyColl

const writeResult: UpdateWriteOpResult = await bountyCollection.updateOne(assignedBounty, {
$set: {
assign: request.assign,
assignedName: assignedUser.user.tag
assignTo: {
discordId: request.assign,
discordHandle: assignedUser.user.tag,
iconUrl: assignedUser.user.avatarURL(),
},
},
});

Expand All @@ -94,10 +97,10 @@ export const assignedBountyMessage = async (message: Message, appliedForBounty:
const embedOrigMessage: MessageEmbed = message.embeds[0];
embedOrigMessage.setTitle(await BountyUtils.createPublicTitle(<Bounty>appliedForBounty));
embedOrigMessage.setFooter({text: '🏴 - claim | ❌ - delete'});
await message.edit({ embeds: [embedOrigMessage] });
await message.reactions.removeAll();
await message.react('🏴');
await message.react('❌');
const componentActions = new MessageActionRow().addComponents(['👷', '📝', '🔄'].map(a =>
new MessageButton().setEmoji(a).setStyle('SECONDARY').setCustomId(a)
))
await message.edit({ embeds: [embedOrigMessage], components: [componentActions] });

};

11 changes: 5 additions & 6 deletions src/app/activity/bounty/Claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Clients } from '../../constants/clients';
import { UserCollection } from '../../types/user/UserCollection';
import ValidationError from '../../errors/ValidationError';
import TimeoutError from '../../errors/TimeoutError';
import DMPermissionError from '../../errors/DMPermissionError';

export const claimBounty = async (request: ClaimRequest): Promise<any> => {
Log.debug('In Claim activity');
Expand All @@ -21,16 +22,14 @@ export const claimBounty = async (request: ClaimRequest): Promise<any> => {
Log.info(`${request.bountyId} bounty claimed by ${claimedByUser.user.tag}`);

if (! (await BountyUtils.isUserWalletRegistered(request.userId))) {
if (request.commandContext) {
const gotoDMMessage = 'Go to your DMs to finish claiming the bounty...';
await request.commandContext.send({ content: gotoDMMessage, ephemeral: true});
}
const gotoDMMessage = 'Go to your DMs to finish claiming the bounty...';
await DiscordUtils.activityResponse(request.commandContext, request.buttonInteraction, gotoDMMessage);

const durationMinutes = 2;
const claimWalletMessage = `Hello <@${request.userId}>!\n` +
`Please respond within 2 minutes.\n` +
`To claim this bounty, please enter the ethereum wallet address (non-ENS) to receive the reward amount for this bounty`;
const walletNeededMessage: Message = await claimedByUser.send({ content: claimWalletMessage });
const walletNeededMessage: Message = await claimedByUser.send({ content: claimWalletMessage }).catch(() => { throw new DMPermissionError(claimWalletMessage) });
const dmChannel: DMChannel = await walletNeededMessage.channel.fetch() as DMChannel;

try {
Expand Down Expand Up @@ -80,7 +79,7 @@ export const claimBounty = async (request: ClaimRequest): Promise<any> => {
await DiscordUtils.activityNotification(creatorNotification, createdByUser);

const claimaintResponse = `<@${claimedByUser.user.id}>, you have claimed this bounty! Reach out to <@${createdByUser.user.id}> with any questions: <${claimedBountyCard.url}>`;
await DiscordUtils.activityResponse(request.commandContext, claimaintResponse , claimedByUser);
await DiscordUtils.activityResponse(request.commandContext, request.buttonInteraction, claimaintResponse);

return;
};
Expand Down
4 changes: 2 additions & 2 deletions src/app/activity/bounty/Complete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const completeBounty = async (request: CompleteRequest): Promise<void> =>
`This bounty is now complete.\n`;

if (!getDbResult.dbBountyResult.paidStatus || getDbResult.dbBountyResult.paidStatus === PaidStatus.unpaid) {
creatorCompleteDM = creatorCompleteDM.concat(`Please remember to mark this bounty as paid (💰)and pay <@${submittedByUser.id}>`);
creatorCompleteDM = creatorCompleteDM.concat(`Please remember to mark this bounty as paid (💰) and pay <@${submittedByUser.id}>`);
}
else {
creatorCompleteDM = creatorCompleteDM.concat(
Expand All @@ -53,7 +53,7 @@ export const completeBounty = async (request: CompleteRequest): Promise<void> =>
submitterCompleteDM = submitterCompleteDM.concat(`<@${completedByUser.id}> should be paying you with ${getDbResult.dbBountyResult.reward.amount} ${getDbResult.dbBountyResult.reward.currency} soon.`);
}
await DiscordUtils.activityNotification(submitterCompleteDM, submittedByUser);
await DiscordUtils.activityResponse(request.commandContext, creatorCompleteDM, completedByUser);
await DiscordUtils.activityResponse(request.commandContext, request.buttonInteraction, creatorCompleteDM);
return;
}

Expand Down
29 changes: 19 additions & 10 deletions src/app/activity/bounty/Create.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Bounty } from '../../types/bounty/Bounty';
import Log from '../../utils/Log';
import { Message, GuildMember, DMChannel, AwaitMessagesOptions } from 'discord.js';
import { Message, GuildMember, DMChannel, AwaitMessagesOptions, Role } from 'discord.js';
import DiscordUtils from '../../utils/DiscordUtils';
import BountyUtils from '../../utils/BountyUtils';
import MongoDbUtils from '../../utils/MongoDbUtils';
Expand Down Expand Up @@ -169,6 +169,8 @@ export const createBounty = async (createRequest: CreateRequest): Promise<any> =
}
}
}

await DiscordUtils.activityResponse(createRequest.commandContext, null, 'IOU created successfully');
} else {

const publishOrDeleteMessage =
Expand All @@ -193,17 +195,19 @@ const createDbHandler = async (
const db: Db = await MongoDbUtils.connect('bountyboard');
const dbBounty = db.collection('bounties');

if (createRequest.assign) {
createRequest.assignedName = (await DiscordUtils.getGuildMemberFromUserId(createRequest.assign, createRequest.guildId)).displayName;
}
const assignedTo: GuildMember = createRequest.assign ? await DiscordUtils.getGuildMemberFromUserId(createRequest.assign, createRequest.guildId) : null;

const createdBounty: Bounty = generateBountyRecord(
const gatedTo: Role = createRequest.gate ? await DiscordUtils.getRoleFromRoleId(createRequest.gate, createRequest.guildId) : null;

const createdBounty: Bounty = await generateBountyRecord(
createRequest,
description,
criteria,
dueAt,
guildMember,
owedTo,
assignedTo,
gatedTo,
createdInChannel);


Expand All @@ -217,15 +221,17 @@ const createDbHandler = async (

}

export const generateBountyRecord = (
export const generateBountyRecord = async (
createRequest: CreateRequest,
description: string,
criteria: string,
dueAt: Date,
guildMember: GuildMember,
owedTo: GuildMember,
assignedTo: GuildMember,
gatedTo: Role,
createdInChannel: string
): Bounty => {
): Promise<Bounty> => {

Log.debug('generating bounty record')
const [reward, symbol] = (createRequest.reward != null) ? createRequest.reward.split(' ') : [null, null];
Expand Down Expand Up @@ -273,7 +279,7 @@ export const generateBountyRecord = (
};

if (createRequest.gate) {
bountyRecord.gate = [createRequest.gate]
bountyRecord.gateTo = [{discordId: gatedTo.id, discordName: gatedTo.name, iconUrl: gatedTo.iconURL()}];
}

if (createRequest.evergreen) {
Expand All @@ -285,8 +291,11 @@ export const generateBountyRecord = (
}

if (createRequest.assign) {
bountyRecord.assign = createRequest.assign;
bountyRecord.assignedName = createRequest.assignedName;
bountyRecord.assignTo = {
discordId: assignedTo.user.id,
discordHandle: assignedTo.user.tag,
iconUrl: assignedTo.user.avatarURL(),
}
}

if (createRequest.requireApplication) {
Expand Down
2 changes: 1 addition & 1 deletion src/app/activity/bounty/Delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const deleteBounty = async (request: DeleteRequest): Promise<void> => {
getDbResult.dbBountyResult.childrenIds !== undefined && getDbResult.dbBountyResult.childrenIds.length > 0) {
creatorDeleteDM += 'Children bounties created from this multi-claimant bounty will remain.\n';
}
await DiscordUtils.activityResponse(request.commandContext, creatorDeleteDM, deletedByUser);
await DiscordUtils.activityResponse(request.commandContext, request.buttonInteraction, creatorDeleteDM);
return;
}

Expand Down
11 changes: 11 additions & 0 deletions src/app/activity/bounty/Handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import AuthorizationModule from "../../auth/commandAuth";
import ValidationModule from "../../validation/commandValidation";
import { BountyActivityHandler } from "./ActivityHandler";
import Log from '../../utils/Log';
import BountyUtils from "../../utils/BountyUtils";

/**
* handler is responsible for the flow of any activity request.
Expand All @@ -14,9 +15,19 @@ import Log from '../../utils/Log';
export const handler = async (request: any): Promise<void> => {
Log.debug(`In Handler: Bounty ID: ${request.bountyId} Actvity: ${request.activity}`);

setTimeout(async ()=> {
if (request.buttonInteraction && !(request.buttonInteraction.replied || request.buttonInteraction.deferred)) {
await request.buttonInteraction.deferReply({ ephemeral: true }).catch(e => Log.debug(`Error: ${e.message}`));
} else if (request.commandContext && !request.commandContext.initiallyResponded) {
await request.commandContext.defer();
}
}, 2000);

await ValidationModule.run(request);

await AuthorizationModule.run(request);

await BountyActivityHandler.run(request);

if (request.bountyId) await BountyUtils.bountyCleanUp(request.bountyId);
}
6 changes: 3 additions & 3 deletions src/app/activity/bounty/Help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const helpBounty = async (request: HelpRequest): Promise<void> => {

const bountyCreator: GuildMember = await DiscordUtils.getGuildMemberFromUserId(getDbResult.dbBountyResult.createdBy.discordId, request.guildId)

const bountyUrl = process.env.BOUNTY_BOARD_URL + request.bountyId;
const bountyUrl = (request.message && request.message.url) || (process.env.BOUNTY_BOARD_URL + request.bountyId);
const creatorHelpDM =
`<@${helpRequestedUser.id}> has requested help with the following bounty:\n` +
`<${bountyUrl}>\n` +
Expand All @@ -34,8 +34,8 @@ export const helpBounty = async (request: HelpRequest): Promise<void> => {
`<@${bountyCreator.id}> has been notified of your request for help with the following bounty:\n` +
`<${bountyUrl}>`;

await bountyCreator.send({ content: creatorHelpDM});
await helpRequestedUser.send({ content: userHelpDM });
await bountyCreator.send({ content: creatorHelpDM });
await DiscordUtils.activityResponse(request.commandContext, request.buttonInteraction, userHelpDM);
} else {
const bountyChannel: TextChannel = await client.channels.fetch(request.commandContext.channelID) as TextChannel;

Expand Down
Loading

0 comments on commit 033bcd7

Please sign in to comment.