@@ -1,6 +1,8 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { GeminiService } from "../gemini/gemini.service";
|
||||
import { PredictionCardDto } from "./dto/prediction-card.dto";
|
||||
import axios from "axios";
|
||||
|
||||
const SYSTEM_PROMPT = `Sen profesyonel bir spor analisti ve sosyal medya içerik üreticisisin.
|
||||
Verilen maç tahmin verisini kullanarak kısa, etkili ve ilgi çekici sosyal medya postları yazıyorsun.
|
||||
@@ -11,6 +13,7 @@ KURALLAR:
|
||||
- Emoji kullan ama abartma (2-4 emoji yeterli)
|
||||
- Skor tahminini vurgula
|
||||
- Güven yüzdesini belirt
|
||||
- Lig, ülke, takım adları ve ana tahminleri SEO için doğal şekilde geçir
|
||||
- İlgili hashtag'leri ekle (#PremierLeague, #SüperLig vb.)
|
||||
- KESİNLİKLE "kesin kazanır", "garanti" gibi ifadeler KULLANMA
|
||||
- "Tahminimiz", "Beklentimiz", "Analizimiz" gibi ifadeler kullan
|
||||
@@ -20,13 +23,31 @@ KURALLAR:
|
||||
@Injectable()
|
||||
export class CaptionGeneratorService {
|
||||
private readonly logger = new Logger(CaptionGeneratorService.name);
|
||||
private readonly ollamaBaseUrl: string;
|
||||
private readonly ollamaModel: string;
|
||||
|
||||
constructor(private readonly geminiService: GeminiService) {}
|
||||
constructor(
|
||||
private readonly geminiService: GeminiService,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
this.ollamaBaseUrl =
|
||||
this.configService.get<string>("OLLAMA_BASE_URL") ||
|
||||
"http://localhost:11434";
|
||||
this.ollamaModel =
|
||||
this.configService.get<string>("OLLAMA_MODEL") ||
|
||||
this.configService.get<string>("SOCIAL_POSTER_OLLAMA_MODEL") ||
|
||||
"";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a social media caption for a match prediction using Gemini AI.
|
||||
*/
|
||||
async generateCaption(card: PredictionCardDto): Promise<string> {
|
||||
if (this.ollamaModel) {
|
||||
const caption = await this.generateWithOllama(card);
|
||||
if (caption) return caption;
|
||||
}
|
||||
|
||||
if (!this.geminiService.isAvailable()) {
|
||||
this.logger.warn("Gemini not available, using template caption");
|
||||
return this.generateFallbackCaption(card);
|
||||
@@ -53,6 +74,39 @@ export class CaptionGeneratorService {
|
||||
}
|
||||
}
|
||||
|
||||
private async generateWithOllama(card: PredictionCardDto): Promise<string> {
|
||||
const prompt = `${SYSTEM_PROMPT}
|
||||
|
||||
${this.buildPrompt(card)}`;
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.ollamaBaseUrl.replace(/\/$/, "")}/api/generate`,
|
||||
{
|
||||
model: this.ollamaModel,
|
||||
prompt,
|
||||
stream: false,
|
||||
options: {
|
||||
temperature: 0.7,
|
||||
num_predict: 260,
|
||||
},
|
||||
},
|
||||
{ timeout: 20000 },
|
||||
);
|
||||
|
||||
const text = String(response.data?.response || "").trim();
|
||||
if (!text) return "";
|
||||
|
||||
this.logger.log(
|
||||
`Ollama caption generated for ${card.homeTeam} vs ${card.awayTeam}`,
|
||||
);
|
||||
return this.ensureHashtags(text, card);
|
||||
} catch (error) {
|
||||
this.logger.warn(`Ollama caption generation failed: ${error.message}`);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private buildPrompt(card: PredictionCardDto): string {
|
||||
const topPicksText = card.topPicks
|
||||
.map(
|
||||
@@ -64,9 +118,11 @@ export class CaptionGeneratorService {
|
||||
return `Aşağıdaki maç tahmin verisini kullanarak bir sosyal medya postu oluştur:
|
||||
|
||||
MAÇ: ${card.homeTeam} vs ${card.awayTeam}
|
||||
SPOR: ${card.sport === "basketball" ? "Basketbol" : "Futbol"}
|
||||
LİG: ${card.leagueName}
|
||||
ÜLKE/BÖLGE: ${card.countryName || "-"}
|
||||
TARİH: ${card.matchDate}
|
||||
İLK YARI SKOR TAHMİNİ: ${card.htScore}
|
||||
${card.sport === "basketball" ? "İLK DEVRE" : "İLK YARI"} SKOR TAHMİNİ: ${card.htScore}
|
||||
MAÇ SONU SKOR TAHMİNİ: ${card.ftScore}
|
||||
SKOR GÜVEN: %${card.scoreConfidence}
|
||||
RİSK SEVİYESİ: ${card.riskLevel}
|
||||
@@ -85,7 +141,8 @@ Sadece post metnini yaz, başka hiçbir şey ekleme.`;
|
||||
.replace(/[^a-zA-Z0-9üöçşğıİÜÖÇŞĞ]/g, "");
|
||||
const homeTag = card.homeTeam.replace(/\s+/g, "");
|
||||
const awayTag = card.awayTeam.replace(/\s+/g, "");
|
||||
text += `\n\n#${leagueTag} #${homeTag} #${awayTag}`;
|
||||
const sportTag = card.sport === "basketball" ? "Basketbol" : "Futbol";
|
||||
text += `\n\n#${leagueTag} #${homeTag} #${awayTag} #${sportTag}`;
|
||||
}
|
||||
return text.trim();
|
||||
}
|
||||
@@ -99,11 +156,14 @@ Sadece post metnini yaz, başka hiçbir şey ekleme.`;
|
||||
.replace(/\s+/g, "")
|
||||
.replace(/[^a-zA-Z0-9üöçşğıİÜÖÇŞĞ]/g, "");
|
||||
|
||||
return `⚡ ${card.homeTeam} vs ${card.awayTeam}
|
||||
🎯 Tahminimiz: ${card.ftScore} (İY: ${card.htScore})
|
||||
const sportLabel = card.sport === "basketball" ? "Basketbol" : "Futbol";
|
||||
const halfLabel = card.sport === "basketball" ? "İD" : "İY";
|
||||
|
||||
return `⚡ ${card.leagueName}${card.countryName ? ` (${card.countryName})` : ""}: ${card.homeTeam} vs ${card.awayTeam}
|
||||
🎯 ${sportLabel} tahminimiz: ${card.ftScore} (${halfLabel}: ${card.htScore})
|
||||
📊 Güven: %${card.scoreConfidence}
|
||||
${topPick ? `🔥 ${topPick.market}: ${topPick.pick} (%${topPick.confidence})` : ""}
|
||||
|
||||
#${leagueTag} #SuggestBet #Bahis`.trim();
|
||||
#${leagueTag} #${sportLabel} #MaçTahmini #iddaai`.trim();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user