Skip to main content

max / balanced_breakfast

5.0 KB · 219 lines History Blame Raw
1 // GitHub trending repos. Approximates trending by searching the GitHub
2 // search API for repos with >100 stars, sorted by star count. Optional
3 // language filter; no auth required. Formats star counts as "Nk" display.
4
5 const GH_API = "https://api.github.com/search/repositories";
6
7 fn id() {
8 "github_trending"
9 }
10
11 fn name() {
12 "GitHub Trending"
13 }
14
15 fn capabilities() {
16 #{
17 supports_pagination: true
18 }
19 }
20
21 fn config_schema() {
22 #{
23 description: "Trending GitHub repositories by stars gained recently. Filter by language.",
24 fields: [
25 #{
26 key: "language",
27 label: "Language",
28 field_type: "text",
29 description: "Filter by language (e.g., rust, python, javascript). Leave blank for all.",
30 placeholder: "rust"
31 },
32 #{
33 key: "timeframe",
34 label: "Time Window",
35 field_type: "select",
36 required: true,
37 description: "How far back to look for star activity",
38 default_value: "weekly",
39 options: ["daily", "weekly", "monthly"]
40 },
41 #{
42 key: "limit",
43 label: "Repos to show",
44 field_type: "text",
45 description: "Number of repos to fetch (max 30)",
46 default_value: "20",
47 placeholder: "20"
48 }
49 ]
50 }
51 }
52
53 fn fetch(config, cursor) {
54 let limit = 20;
55 if config.limit != () {
56 let parsed = parse_int(config.limit);
57 if parsed != () && parsed > 0 {
58 if parsed > 30 {
59 limit = 30;
60 } else {
61 limit = parsed;
62 }
63 }
64 }
65
66 let page = 1;
67 if cursor != () {
68 let parsed = parse_int(cursor);
69 if parsed != () && parsed > 0 {
70 page = parsed;
71 }
72 }
73
74 // Build date query for "created after" threshold
75 let timeframe = "weekly";
76 if config.timeframe != () {
77 timeframe = config.timeframe;
78 }
79
80 // Use stars>100 as a proxy for trending (GitHub search API)
81 let query = "stars:>100";
82
83 if config.language != () && config.language != "" {
84 query += " language:" + config.language;
85 }
86
87 // Sort by stars to approximate trending
88 let url = GH_API + "?q=" + query + "&sort=stars&order=desc&per_page=" + limit + "&page=" + page;
89
90 let data = http_get_json(url);
91
92 if data == () {
93 return #{ items: [], has_more: false };
94 }
95
96 let repos = data.items;
97 if repos == () {
98 return #{ items: [], has_more: false };
99 }
100
101 let items = [];
102 for repo in repos {
103 let item = parse_repo(repo);
104 if item != () {
105 items.push(item);
106 }
107 }
108
109 let has_more = items.len() >= limit;
110 let result = #{
111 items: items,
112 has_more: has_more
113 };
114
115 if has_more {
116 result.next_cursor = "" + (page + 1);
117 }
118
119 result
120 }
121
122 fn parse_repo(repo) {
123 if repo.full_name == () {
124 return ();
125 }
126
127 let name = repo.full_name;
128 let description = "";
129 if repo.description != () {
130 description = repo.description;
131 }
132
133 let stars = 0;
134 if repo.stargazers_count != () {
135 stars = repo.stargazers_count;
136 }
137
138 let forks = 0;
139 if repo.forks_count != () {
140 forks = repo.forks_count;
141 }
142
143 let language = "";
144 if repo.language != () {
145 language = repo.language;
146 }
147
148 let url = "";
149 if repo.html_url != () {
150 url = repo.html_url;
151 }
152
153 let owner = "Unknown";
154 if repo.owner != () {
155 if repo.owner.login != () {
156 owner = repo.owner.login;
157 }
158 }
159
160 let created = timestamp_now();
161 if repo.pushed_at != () {
162 created = repo.pushed_at;
163 }
164
165 let repo_id = 0;
166 if repo.id != () {
167 repo_id = repo.id;
168 }
169
170 // Format stars count
171 let stars_display = "" + stars;
172 if stars >= 1000 {
173 stars_display = "" + (stars / 1000) + "k";
174 }
175
176 let secondary = stars_display + " stars · " + forks + " forks";
177 if language != "" {
178 secondary += " · " + language;
179 }
180
181 // Build body
182 let body = "";
183 if description != "" {
184 body = description + "\n\n";
185 }
186 body += "**Stars:** " + stars + "\n";
187 body += "**Forks:** " + forks + "\n";
188 if language != "" {
189 body += "**Language:** " + language + "\n";
190 }
191
192 // Tags
193 let tags = ["github"];
194 if language != "" {
195 tags.push(language);
196 }
197
198 #{
199 id: #{ source: "github_trending", item_id: "" + repo_id },
200 bite: #{
201 author: owner,
202 text: truncate(name + ": " + description, 100),
203 secondary: secondary,
204 indicator: "⭐"
205 },
206 content: #{
207 title: name,
208 body: body,
209 url: url
210 },
211 meta: #{
212 source_name: "GitHub Trending",
213 published_at: created,
214 score: stars,
215 tags: tags
216 }
217 }
218 }
219