diff --git a/.env.example b/.env.example index 7bf10e33..b8374150 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ DATABASE_URL="postgresql://…" -ADMINS="0x…,0x…,0x…" \ No newline at end of file +ADMINS="0x…,0x…,0x…" +OPENAI_API_KEY="sk-…" \ No newline at end of file diff --git a/src/app/api/generateBounty/route.ts b/src/app/api/generateBounty/route.ts new file mode 100644 index 00000000..ab66e776 --- /dev/null +++ b/src/app/api/generateBounty/route.ts @@ -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 { + 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); +} diff --git a/src/components/global/FormBounty.tsx b/src/components/global/FormBounty.tsx index 912fafcd..9e42ba9c 100644 --- a/src/components/global/FormBounty.tsx +++ b/src/components/global/FormBounty.tsx @@ -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, @@ -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 ( <> @@ -129,19 +158,33 @@ export default function FormBounty({ > - title + + title + 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' /> - description + + description + reward @@ -198,6 +241,16 @@ export default function FormBounty({ create bounty +
+ need a bouty idea? click the + +
);