diff --git a/.env.example b/.env.example index da43a76..0b88a49 100644 --- a/.env.example +++ b/.env.example @@ -14,3 +14,4 @@ PINATA_GATEWAY= # Change to your website URL # For development: set to http://localhost:3000 SITE_URL=https://memes.markto.market +ACCOUNT_HANDLE=mark_2_market1 diff --git a/src/app/api/tweet/route.ts b/src/app/api/tweet/route.ts new file mode 100644 index 0000000..b44d0ae --- /dev/null +++ b/src/app/api/tweet/route.ts @@ -0,0 +1,47 @@ +import { NextRequest, NextResponse } from 'next/server' +import { verifySignatureInTweet } from '../../../utils/verifyTweet'; + +export async function POST(req: NextRequest): Promise { + try { + const { tweetUrl } = await req.json(); + + const url = `https://publish.twitter.com/oembed?url=${tweetUrl}&maxwidth=600`; + + const response = await fetch(url); + const data = await response.json(); + const { handle, memeUrl, txHash } = extractData(data.html); + if (!handle || !memeUrl || !txHash) { + return NextResponse.json( + { error: 'Verification failed' }, + { status: 500 } + ) + } + + const isVerified = await verifySignatureInTweet(txHash); + const isHandleCorrect = handle === process.env.ACCOUNT_HANDLE; + + return NextResponse.json({isVerified: isVerified && isHandleCorrect}) + } catch (error) { + console.error('Error while verifying tweet:', error) + return NextResponse.json( + { error: 'Failed to verify tweet' }, + { status: 500 } + ) + } +} + +const extractData = (tweet: string | object) => { + const tweetText = typeof tweet === 'string' ? tweet : JSON.stringify(tweet); + + const decodedTweet = tweetText.replace(/'/g, "'").replace(/"/g, '"'); + + const urlMatch = decodedTweet.match(//); + const txHashMatch = decodedTweet.match(/TX Hash: '([^']+)'/); + const handleMatch = decodedTweet.match(/@([A-Za-z0-9_]+)/); + + return { + memeUrl: urlMatch ? urlMatch[1] : null, + txHash: txHashMatch ? txHashMatch[1].trim() : null, + handle: handleMatch ? handleMatch[1] : null, + }; +}; diff --git a/src/components/AIServiceCard.tsx b/src/components/AIServiceCard.tsx index 12f99e1..8658c03 100644 --- a/src/components/AIServiceCard.tsx +++ b/src/components/AIServiceCard.tsx @@ -4,11 +4,13 @@ import React, { useState } from 'react' import BN from 'bn.js'; import Big from 'big.js'; +import TweetUrlForm from './TweetForm'; + interface AIServiceCardProps { title: string description: string isWalletConnected: boolean - onGenerate: (prompt: string) => Promise<{ imageUrl?: string, error?: string }> + onGenerate: (prompt: string) => Promise<{ imageUrl?: string, transactionSignature?: string, error?: string }> priceMTM: BN } @@ -16,6 +18,7 @@ interface GenerationState { loading: boolean processing: boolean imageUrl: string | null + transactionSignature: string | null error: string | null } @@ -38,6 +41,7 @@ const AIServiceCard: React.FC = ({ loading: false, processing: false, imageUrl: null, + transactionSignature: null, error: null, }) @@ -67,11 +71,12 @@ const AIServiceCard: React.FC = ({ return } - if (result.imageUrl) { + if (result.imageUrl && result.transactionSignature) { setGenerationState({ loading: false, processing: false, imageUrl: result.imageUrl, + transactionSignature: result.transactionSignature, error: null, }) } else { @@ -86,7 +91,7 @@ const AIServiceCard: React.FC = ({ } } - const generateTwitterShareUrl = (imageUrl: string): string => { + const generateTwitterShareUrl = (imageUrl: string, transactionSignature: string): string => { const baseUrl = window.location.href; const ipfsImageUrl = imageUrl.split("/ipfs/")[1]; const memeUrl = `${baseUrl}/memes/${ipfsImageUrl}`; @@ -94,6 +99,17 @@ const AIServiceCard: React.FC = ({ return `https://twitter.com/intent/tweet?text=Check%20out%20this%20meme%20I%20generated!&url=${encodeURIComponent(memeUrl)}`; }; + // const generateTwitterShareUrl = (imageUrl: string, transactionSignature: string): string => { + // const baseUrl = window.location.href; + // const ipfsImageUrl = imageUrl.split("/ipfs/")[1]; + // const memeUrl = `${baseUrl}/memes/${ipfsImageUrl}`; + + // // Ensure the entire tweet text is properly URL-encoded + // const tweetText = `Check out this meme that I generated! TX Hash: ${transactionSignature} @mark_2_market1`; + + // return `https://twitter.com/intent/tweet?text=${encodeURIComponent(tweetText)}&url=${encodeURIComponent(memeUrl)}`; + // }; + return (
@@ -137,7 +153,7 @@ const AIServiceCard: React.FC = ({
)} - {generationState.imageUrl && ( + {generationState.imageUrl && generationState.transactionSignature && (
= ({ />
{ + const [inputText, setInputText] = useState('') + + const handleGenerate = async (): Promise => { + try { + const response = await fetch('/api/tweet', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ tweetUrl: inputText }), + }) + + if (!response.ok) { + throw new Error(`Failed to verify tweet: ${response.statusText}`); + } + + } catch (error) { + console.error('Failed to fetch price:', error); + } + } + + return ( +
+
+
+ +
+ +
+