Skip to content

Commit

Permalink
Merge pull request #416 from whtsupbab3/332-generate-bounty-idea
Browse files Browse the repository at this point in the history
332 generate bounty idea
  • Loading branch information
picsoritdidnthappen authored Dec 10, 2024
2 parents d8457e9 + 43b3a56 commit 60e365b
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
DATABASE_URL="postgresql://…"
ADMINS="0x…,0x…,0x…"
ADMINS="0x…,0x…,0x…"
OPENAI_API_KEY="sk-…"
45 changes: 45 additions & 0 deletions src/app/api/generateBounty/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const prompt = `Generate unique, creative, and fun bounty ideas for the "Pics or It Didn't Happen" (poidh) website. Each bounty should encourage users to engage in amusing, interesting, or surprising activities that can be easily documented with a photo, screenshot, or video.
Ensure the ideas are diverse, spanning different themes such as real-life actions, contributions, playful tasks, or simple creative(could be developer) projects.
Ideas must remain achievable and enjoyable for users of all skill levels. A user should share result either in video or in photo. Include:
Title: A short, catchy description of the bounty (max 50 characters).
Description: A clear and engaging explanation of what the user must do to complete the bounty (max 350 characters).
Return the ideas in JSON format like this:
{ 'title': '...', 'description': '...' }.`;

export async function POST(): Promise<Response> {
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
if (!OPENAI_API_KEY) {
throw new Error('Missing OpenAI API key');
}

const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4-turbo',
messages: [
{
role: 'system',
content: prompt,
},
{
role: 'user',
content: 'Generate a bounty idea for a person to do.',
},
],
max_tokens: 100,
temperature: 1,
response_format: { type: 'json_object' },
}),
});

if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}

const data = await response.json();
return Response.json(data.choices[0].message.content);
}
61 changes: 57 additions & 4 deletions src/components/global/FormBounty.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import GameButton from '@/components/global/GameButton';
import ButtonCTA from '@/components/ui/ButtonCTA';
import { InfoIcon } from '@/components/global/Icons';

type Bounty = {
title: string;
description: string;
};

export default function FormBounty({
open,
onClose,
Expand Down Expand Up @@ -107,6 +112,30 @@ export default function FormBounty({
},
});

const generateBountyMutation = useMutation({
mutationFn: async () => {
setName('Generating…');
setDescription('Generating…');
const res = await fetch('/api/generateBounty', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
return JSON.parse(await res.json()) as Bounty;
},
onSuccess: (bounty: Bounty) => {
setName(bounty.title);
setDescription(bounty.description);
toast.success('Bounty generated successfully');
},
onError: (error) => {
setName('');
setDescription('');
toast.error('Failed to generate bounty: ' + error.message);
},
});

return (
<>
<Loading open={createBountyMutations.isPending} status={status} />
Expand All @@ -129,19 +158,33 @@ export default function FormBounty({
>
<DialogContent>
<Box display='flex' flexDirection='column' width='100%'>
<span>title</span>
<span
className={cn(
generateBountyMutation.isPending && 'animate-pulse'
)}
>
title
</span>
<input
disabled={generateBountyMutation.isPending}
type='text'
value={name}
onChange={(e) => setName(e.target.value)}
className='border bg-transparent border-[#D1ECFF] py-2 px-2 rounded-md mb-4'
className='border py-2 px-2 rounded-md mb-4 bg-transparent border-[#D1ECFF] disabled:cursor-not-allowed disabled:animate-pulse'
/>
<span>description</span>
<span
className={cn(
generateBountyMutation.isPending && 'animate-pulse'
)}
>
description
</span>
<textarea
disabled={generateBountyMutation.isPending}
rows={3}
value={description}
onChange={(e) => setDescription(e.target.value)}
className='border bg-transparent border-[#D1ECFF] py-2 px-2 rounded-md mb-4 max-h-28'
className='border py-2 px-2 rounded-md mb-4 max-h-28 bg-transparent border-[#D1ECFF] disabled:cursor-not-allowed disabled:animate-pulse'
></textarea>

<span>reward</span>
Expand Down Expand Up @@ -198,6 +241,16 @@ export default function FormBounty({
<ButtonCTA>create bounty</ButtonCTA>
</button>
</DialogActions>
<div className='py-4 mt-1 w-full flex justify-center items-center flex-row'>
<span className='mr-2'>need a bouty idea? click the</span>
<button
className='cursor-pointer items-center text-center disabled:cursor-not-allowed'
onClick={() => generateBountyMutation.mutate()}
disabled={generateBountyMutation.isPending}
>
🤖
</button>
</div>
</Dialog>
</>
);
Expand Down

0 comments on commit 60e365b

Please sign in to comment.