|
1017 | 1017 | async function loadSuggestions(){ |
1018 | 1018 | document.getElementById('suggestions-output').innerHTML=`<div class="ai-loading"><div class="typing-dots"><span></span><span></span><span></span></div><span style="margin-left:4px">fetching trending projects...</span></div>`; |
1019 | 1019 |
|
1020 | | - // Get user's top languages |
1021 | | - const langs=Object.entries(langMap).sort((a,b)=>b[1]-a[1]).slice(0,3).map(([l])=>l); |
| 1020 | + const langs=Object.entries(langMap).sort((a,b)=>b[1]-a[1]).slice(0,4).map(([l])=>l); |
1022 | 1021 | const userRepoNames=new Set(repoData.map(r=>r.full_name||r.name)); |
1023 | 1022 |
|
1024 | | - // Keywords that indicate a real project (not a package/lib) |
1025 | | - const projectTopics='topic:hacktoberfest+topic:awesome+topic:cli+topic:tool+topic:bot+topic:security+topic:iot+topic:monitoring+topic:dashboard'; |
| 1023 | + // Large topic pool — pick 3 random ones each refresh |
| 1024 | + const ALL_TOPICS = [ |
| 1025 | + 'topic:cli','topic:tool','topic:bot','topic:security','topic:hacking', |
| 1026 | + 'topic:iot','topic:monitoring','topic:dashboard','topic:terminal', |
| 1027 | + 'topic:automation','topic:devops','topic:api','topic:game','topic:productivity', |
| 1028 | + 'topic:reverse-engineering','topic:penetration-testing','topic:fuzzing', |
| 1029 | + 'topic:network','topic:linux','topic:raspberry-pi','topic:arduino', |
| 1030 | + 'topic:machine-learning','topic:data-science','topic:visualization', |
| 1031 | + 'topic:compiler','topic:interpreter','topic:editor','topic:shell', |
| 1032 | + ]; |
1026 | 1033 |
|
1027 | | - try { |
1028 | | - // Search GitHub for fun/interesting projects in user's languages |
1029 | | - const queries = langs.slice(0,2).map(lang => |
1030 | | - `https://api.github.com/search/repositories?q=language:${encodeURIComponent(lang)}+${projectTopics}+stars:>500+is:public&sort=updated&order=desc&per_page=15` |
1031 | | - ); |
1032 | | - // Also fetch trending security/dev tools regardless of language |
1033 | | - queries.push(`https://api.github.com/search/repositories?q=topic:security+topic:hacking+stars:>200+is:public&sort=updated&order=desc&per_page=10`); |
1034 | | - queries.push(`https://api.github.com/search/repositories?q=topic:developer-tools+topic:cli+stars:>300+is:public&sort=updated&order=desc&per_page=10`); |
| 1034 | + // Shuffle helper |
| 1035 | + function shuffle(arr) { |
| 1036 | + const a = [...arr]; |
| 1037 | + for (let i = a.length-1; i > 0; i--) { |
| 1038 | + const j = Math.floor(Math.random() * (i+1)); |
| 1039 | + [a[i],a[j]] = [a[j],a[i]]; |
| 1040 | + } |
| 1041 | + return a; |
| 1042 | + } |
| 1043 | + |
| 1044 | + // Random star threshold: 100–1000 |
| 1045 | + function randStars() { return [100,200,300,500,1000][Math.floor(Math.random()*5)]; } |
| 1046 | + // Random sort |
| 1047 | + function randSort() { return ['updated','stars'][Math.floor(Math.random()*2)]; } |
| 1048 | + // Random page (1-3) so same query returns different results |
| 1049 | + function randPage() { return Math.floor(Math.random()*3)+1; } |
| 1050 | + |
| 1051 | + // Pick 3 random topics per query |
| 1052 | + const pickedTopics = shuffle(ALL_TOPICS).slice(0,3).join('+'); |
| 1053 | + const pickedTopics2 = shuffle(ALL_TOPICS).slice(0,3).join('+'); |
| 1054 | + const pickedTopics3 = shuffle(ALL_TOPICS).slice(0,3).join('+'); |
1035 | 1055 |
|
| 1056 | + // Shuffle langs and pick randomly |
| 1057 | + const shuffledLangs = shuffle(langs); |
| 1058 | + |
| 1059 | + try { |
1036 | 1060 | const headers={'Authorization':`token ${ghToken}`,'Accept':'application/vnd.github.v3+json'}; |
| 1061 | + |
| 1062 | + const queries = [ |
| 1063 | + // User's top language + random topics |
| 1064 | + `https://api.github.com/search/repositories?q=language:${encodeURIComponent(shuffledLangs[0]||'Python')}+${pickedTopics}+stars:>${randStars()}+is:public&sort=${randSort()}&order=desc&per_page=15&page=${randPage()}`, |
| 1065 | + // Second language if exists |
| 1066 | + shuffledLangs[1] |
| 1067 | + ? `https://api.github.com/search/repositories?q=language:${encodeURIComponent(shuffledLangs[1])}+${pickedTopics2}+stars:>${randStars()}+is:public&sort=${randSort()}&order=desc&per_page=15&page=${randPage()}` |
| 1068 | + : `https://api.github.com/search/repositories?q=${pickedTopics2}+stars:>500+is:public&sort=updated&order=desc&per_page=15&page=${randPage()}`, |
| 1069 | + // Pure random topic set — no language filter |
| 1070 | + `https://api.github.com/search/repositories?q=${pickedTopics3}+stars:>${randStars()}+is:public&sort=${randSort()}&order=desc&per_page=15&page=${randPage()}`, |
| 1071 | + ]; |
| 1072 | + |
1037 | 1073 | const results = await Promise.all(queries.map(url=>fetch(url,{headers}).then(r=>r.json()).catch(()=>({items:[]})))); |
1038 | 1074 |
|
1039 | | - // Flatten, dedupe, filter out user's own repos and pure libraries |
1040 | | - const SKIP_KEYWORDS = ['sdk','npm','package','library','lib','boilerplate','template','framework','starter','awesome-list','cheatsheet','course','tutorial','learning']; |
| 1075 | + const SKIP_KEYWORDS = ['sdk','npm','package','library','lib','boilerplate','template','framework','starter','awesome-list','cheatsheet','course','tutorial','learning','docs','documentation']; |
1041 | 1076 | const seen = new Set(); |
1042 | 1077 | let all = []; |
1043 | 1078 | results.forEach(r=>{ |
|
1051 | 1086 | }); |
1052 | 1087 | }); |
1053 | 1088 |
|
1054 | | - // Sort by recency + stars combined, pick top 6 |
| 1089 | + // Shuffle the pool first so same-scored repos vary between refreshes |
| 1090 | + all = shuffle(all); |
| 1091 | + |
| 1092 | + // Score by stars + recency but with some randomness |
1055 | 1093 | all.sort((a,b)=>{ |
1056 | | - const scoreA = a.stargazers_count * 0.3 + (new Date(a.pushed_at).getTime()/1e11); |
1057 | | - const scoreB = b.stargazers_count * 0.3 + (new Date(b.pushed_at).getTime()/1e11); |
| 1094 | + const scoreA = a.stargazers_count * 0.2 + (new Date(a.pushed_at).getTime()/1e11) + Math.random()*5; |
| 1095 | + const scoreB = b.stargazers_count * 0.2 + (new Date(b.pushed_at).getTime()/1e11) + Math.random()*5; |
1058 | 1096 | return scoreB - scoreA; |
1059 | 1097 | }); |
1060 | 1098 | const picks = all.slice(0,6); |
|
0 commit comments