android-watcher 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. android_watcher/__init__.py +10 -0
  2. android_watcher/catalog/__init__.py +32 -0
  3. android_watcher/catalog/catalog.toml +531 -0
  4. android_watcher/cli.py +161 -0
  5. android_watcher/config.py +262 -0
  6. android_watcher/detect/__init__.py +1 -0
  7. android_watcher/detect/_normalize.py +192 -0
  8. android_watcher/detect/android_sitemap.py +540 -0
  9. android_watcher/detect/base.py +14 -0
  10. android_watcher/detect/content.py +99 -0
  11. android_watcher/detect/feed.py +135 -0
  12. android_watcher/detect/sitemap.py +203 -0
  13. android_watcher/doctor.py +125 -0
  14. android_watcher/fetch.py +162 -0
  15. android_watcher/group.py +79 -0
  16. android_watcher/lock.py +32 -0
  17. android_watcher/models.py +156 -0
  18. android_watcher/notify/__init__.py +1 -0
  19. android_watcher/notify/base.py +21 -0
  20. android_watcher/notify/email.py +52 -0
  21. android_watcher/notify/html.py +114 -0
  22. android_watcher/notify/render.py +239 -0
  23. android_watcher/notify/slack.py +124 -0
  24. android_watcher/notify/telegram.py +46 -0
  25. android_watcher/rank.py +84 -0
  26. android_watcher/registry.py +38 -0
  27. android_watcher/run.py +283 -0
  28. android_watcher/schedule.py +488 -0
  29. android_watcher/seed/__init__.py +45 -0
  30. android_watcher/seed/seed.sql.gz +0 -0
  31. android_watcher/store.py +492 -0
  32. android_watcher/triage/__init__.py +1 -0
  33. android_watcher/triage/base.py +25 -0
  34. android_watcher/triage/claude_cli.py +185 -0
  35. android_watcher/triage/noop.py +24 -0
  36. android_watcher/tui/__init__.py +1 -0
  37. android_watcher/tui/app.py +163 -0
  38. android_watcher/tui/configio.py +215 -0
  39. android_watcher/tui/screens.py +927 -0
  40. android_watcher-1.0.0.dist-info/METADATA +310 -0
  41. android_watcher-1.0.0.dist-info/RECORD +44 -0
  42. android_watcher-1.0.0.dist-info/WHEEL +4 -0
  43. android_watcher-1.0.0.dist-info/entry_points.txt +2 -0
  44. android_watcher-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,10 @@
1
+ """android-watcher: watch official Google Android sites and deliver a ranked digest."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib.metadata import PackageNotFoundError, version
6
+
7
+ try:
8
+ __version__ = version("android-watcher")
9
+ except PackageNotFoundError: # running from a source tree without installed metadata
10
+ __version__ = "0.0.0"
@@ -0,0 +1,32 @@
1
+ """Packaged source catalog loader."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import tomllib
6
+ from importlib.resources import files
7
+
8
+ from android_watcher.models import Source
9
+
10
+
11
+ def load_catalog() -> list[Source]:
12
+ """Load and return all sources from the packaged catalog.toml."""
13
+ data = files(__name__).joinpath("catalog.toml").read_bytes()
14
+ raw = tomllib.loads(data.decode())
15
+ return [
16
+ Source(
17
+ id=e["id"],
18
+ name=e["name"],
19
+ category=e["category"],
20
+ detector=e["detector"],
21
+ url=e["url"],
22
+ enabled=e.get("enabled", True),
23
+ path_prefix=e.get("path_prefix", ""),
24
+ feed_url=e.get("feed_url", ""),
25
+ content_selector=e.get("content_selector", ""),
26
+ default_weight=e.get("default_weight", 0),
27
+ exclude_prefixes=tuple(e.get("exclude_prefixes", ())),
28
+ require_segment=e.get("require_segment", ""),
29
+ reference_mode=e.get("reference_mode", "index_only"),
30
+ )
31
+ for e in raw.get("source", [])
32
+ ]
@@ -0,0 +1,531 @@
1
+ # android-watcher shipped catalog
2
+ # Curated index of official Google Android / Android-developer sources.
3
+ # Edit this file (and open a PR) to add or change a source. See CONTRIBUTING.md.
4
+ # dac = developer.android.com
5
+ #
6
+ # Fields per [[source]]:
7
+ # id unique slug
8
+ # name human label
9
+ # category one of: platform-release | api-reference | tooling |
10
+ # guides | dev-blog | design | news
11
+ # enabled bool; disabled entries ship but are not watched
12
+ # detector one of: feed | android_sitemap | sitemap | content
13
+ # url canonical page URL
14
+ # path_prefix android_sitemap: match sitemap URLs under this path
15
+ # feed_url feed: RSS/Atom URL. NOTE: every feed_url below is a
16
+ # CANDIDATE and must be confirmed by scripts/verify_catalog.py
17
+ # before release; not yet verified (Appendix A defers these).
18
+ # content_selector content: optional CSS selector ("" = readability)
19
+ # default_weight 0 = use category weight; non-zero overrides
20
+
21
+ # ---------------------------------------------------------------------------
22
+ # Enabled (32)
23
+ # ---------------------------------------------------------------------------
24
+
25
+ [[source]]
26
+ id = "android-versions"
27
+ name = "Android platform release notes"
28
+ category = "platform-release"
29
+ enabled = true
30
+ detector = "android_sitemap"
31
+ url = "https://developer.android.com/about/versions"
32
+ path_prefix = "/about/versions"
33
+ feed_url = ""
34
+ content_selector = ""
35
+ default_weight = 0
36
+
37
+ [[source]]
38
+ id = "source-android"
39
+ name = "Android Open Source Project (source.android.com)"
40
+ category = "platform-release"
41
+ enabled = true
42
+ detector = "android_sitemap"
43
+ url = "https://source.android.com/"
44
+ path_prefix = ""
45
+ feed_url = ""
46
+ content_selector = ""
47
+ default_weight = 0
48
+ exclude_prefixes = ["/reference"]
49
+
50
+ [[source]]
51
+ id = "android-security-bulletins"
52
+ name = "Android Security Bulletins"
53
+ category = "platform-release"
54
+ enabled = true
55
+ detector = "content"
56
+ url = "https://source.android.com/docs/security/bulletin"
57
+ path_prefix = ""
58
+ feed_url = ""
59
+ content_selector = ""
60
+ default_weight = 0
61
+
62
+ [[source]]
63
+ id = "android-studio-releases"
64
+ name = "Android Studio release notes"
65
+ category = "tooling"
66
+ enabled = true
67
+ detector = "android_sitemap"
68
+ url = "https://developer.android.com/studio/releases"
69
+ path_prefix = "/studio/releases"
70
+ feed_url = ""
71
+ content_selector = ""
72
+ default_weight = 0
73
+
74
+ [[source]]
75
+ id = "android-studio-preview"
76
+ name = "Android Studio Canary / preview"
77
+ category = "tooling"
78
+ enabled = true
79
+ detector = "android_sitemap"
80
+ url = "https://developer.android.com/studio/preview"
81
+ path_prefix = "/studio/preview"
82
+ feed_url = ""
83
+ content_selector = ""
84
+ default_weight = 0
85
+
86
+ [[source]]
87
+ id = "agp-releases"
88
+ name = "Android Gradle Plugin release notes"
89
+ category = "tooling"
90
+ enabled = true
91
+ detector = "android_sitemap"
92
+ url = "https://developer.android.com/build/releases/gradle-plugin"
93
+ path_prefix = "/build/releases/gradle-plugin"
94
+ feed_url = ""
95
+ content_selector = ""
96
+ default_weight = 0
97
+
98
+ [[source]]
99
+ id = "platform-tools"
100
+ name = "SDK Platform Tools"
101
+ category = "tooling"
102
+ enabled = true
103
+ detector = "android_sitemap"
104
+ url = "https://developer.android.com/tools/releases/platform-tools"
105
+ path_prefix = "/tools/releases/platform-tools"
106
+ feed_url = ""
107
+ content_selector = ""
108
+ default_weight = 0
109
+
110
+ [[source]]
111
+ id = "build-tools"
112
+ name = "SDK Build Tools"
113
+ category = "tooling"
114
+ enabled = true
115
+ detector = "android_sitemap"
116
+ url = "https://developer.android.com/tools/releases/build-tools"
117
+ path_prefix = "/tools/releases/build-tools"
118
+ feed_url = ""
119
+ content_selector = ""
120
+ default_weight = 0
121
+
122
+ [[source]]
123
+ id = "emulator"
124
+ name = "Emulator release notes"
125
+ category = "tooling"
126
+ enabled = true
127
+ detector = "android_sitemap"
128
+ url = "https://developer.android.com/studio/releases/emulator"
129
+ path_prefix = "/studio/releases/emulator"
130
+ feed_url = ""
131
+ content_selector = ""
132
+ default_weight = 0
133
+
134
+ [[source]]
135
+ id = "ndk"
136
+ name = "NDK revision history"
137
+ category = "tooling"
138
+ enabled = true
139
+ detector = "android_sitemap"
140
+ url = "https://developer.android.com/ndk/downloads/revision_history"
141
+ path_prefix = "/ndk/downloads/revision_history"
142
+ feed_url = ""
143
+ content_selector = ""
144
+ default_weight = 0
145
+
146
+ [[source]]
147
+ id = "androidx-releases"
148
+ name = "AndroidX release notes (aggregate)"
149
+ category = "api-reference"
150
+ enabled = true
151
+ detector = "feed"
152
+ url = "https://developer.android.com/jetpack/androidx/versions"
153
+ path_prefix = ""
154
+ feed_url = "https://developer.android.com/feeds/androidx-release-notes.xml" # CANDIDATE — confirm via scripts/verify_catalog.py before release; not yet verified
155
+ content_selector = ""
156
+ default_weight = 0
157
+
158
+ [[source]]
159
+ id = "compose-releases"
160
+ name = "Jetpack Compose release notes"
161
+ category = "api-reference"
162
+ enabled = true
163
+ detector = "android_sitemap"
164
+ url = "https://developer.android.com/jetpack/androidx/releases/compose"
165
+ path_prefix = "/jetpack/androidx/releases/compose"
166
+ feed_url = ""
167
+ content_selector = ""
168
+ default_weight = 0
169
+
170
+ [[source]]
171
+ id = "android-dev-blog"
172
+ name = "Android Developers Blog"
173
+ category = "dev-blog"
174
+ enabled = true
175
+ detector = "feed"
176
+ url = "https://android-developers.googleblog.com"
177
+ path_prefix = ""
178
+ feed_url = "https://android-developers.googleblog.com/feeds/posts/default" # CANDIDATE — confirm via scripts/verify_catalog.py before release; not yet verified
179
+ content_selector = ""
180
+ default_weight = 0
181
+
182
+ [[source]]
183
+ id = "android-dev-medium"
184
+ name = "Android Developers on Medium"
185
+ category = "dev-blog"
186
+ enabled = true
187
+ detector = "feed"
188
+ url = "https://medium.com/androiddevelopers"
189
+ path_prefix = ""
190
+ feed_url = "https://medium.com/feed/androiddevelopers" # CANDIDATE — confirm via scripts/verify_catalog.py before release; not yet verified
191
+ content_selector = ""
192
+ default_weight = 0
193
+
194
+ [[source]]
195
+ id = "keyword-android"
196
+ name = "The Keyword: Android"
197
+ category = "news"
198
+ enabled = true
199
+ detector = "feed"
200
+ url = "https://blog.google/products-and-platforms/platforms/android/"
201
+ path_prefix = ""
202
+ feed_url = "https://blog.google/products-and-platforms/platforms/android/rss/" # CANDIDATE — confirm via scripts/verify_catalog.py before release; not yet verified
203
+ content_selector = ""
204
+ default_weight = 0
205
+
206
+ [[source]]
207
+ id = "now-in-android"
208
+ name = "Now in Android"
209
+ category = "dev-blog"
210
+ enabled = true
211
+ detector = "android_sitemap"
212
+ url = "https://developer.android.com/series/now-in-android"
213
+ path_prefix = "/series/now-in-android"
214
+ feed_url = ""
215
+ content_selector = ""
216
+ default_weight = 0
217
+
218
+ [[source]]
219
+ id = "android-tv"
220
+ name = "Android TV"
221
+ category = "guides"
222
+ enabled = true
223
+ detector = "android_sitemap"
224
+ url = "https://developer.android.com/tv"
225
+ path_prefix = "/tv"
226
+ feed_url = ""
227
+ content_selector = ""
228
+ default_weight = 0
229
+
230
+ [[source]]
231
+ id = "android-cars"
232
+ name = "Android for Cars"
233
+ category = "guides"
234
+ enabled = true
235
+ detector = "android_sitemap"
236
+ url = "https://developer.android.com/cars"
237
+ path_prefix = "/cars"
238
+ feed_url = ""
239
+ content_selector = ""
240
+ default_weight = 0
241
+
242
+ [[source]]
243
+ id = "android-xr"
244
+ name = "Android XR"
245
+ category = "guides"
246
+ enabled = true
247
+ detector = "android_sitemap"
248
+ url = "https://developer.android.com/xr"
249
+ path_prefix = "/xr"
250
+ feed_url = ""
251
+ content_selector = ""
252
+ default_weight = 0
253
+
254
+ [[source]]
255
+ id = "wear-os"
256
+ name = "Wear OS"
257
+ category = "guides"
258
+ enabled = true
259
+ detector = "android_sitemap"
260
+ url = "https://developer.android.com/wear"
261
+ path_prefix = "/wear"
262
+ feed_url = ""
263
+ content_selector = ""
264
+ default_weight = 0
265
+
266
+ [[source]]
267
+ id = "large-screens"
268
+ name = "Large screens / foldables"
269
+ category = "guides"
270
+ enabled = true
271
+ detector = "android_sitemap"
272
+ url = "https://developer.android.com/guide/topics/large-screens"
273
+ path_prefix = "/guide/topics/large-screens"
274
+ feed_url = ""
275
+ content_selector = ""
276
+ default_weight = 0
277
+
278
+ [[source]]
279
+ id = "health-connect"
280
+ name = "Health Connect"
281
+ category = "guides"
282
+ enabled = true
283
+ detector = "android_sitemap"
284
+ url = "https://developer.android.com/health-and-fitness"
285
+ path_prefix = "/health-and-fitness"
286
+ feed_url = ""
287
+ content_selector = ""
288
+ default_weight = 0
289
+
290
+ [[source]]
291
+ id = "games"
292
+ name = "Android games"
293
+ category = "guides"
294
+ enabled = true
295
+ detector = "android_sitemap"
296
+ url = "https://developer.android.com/games"
297
+ path_prefix = "/games"
298
+ feed_url = ""
299
+ content_selector = ""
300
+ default_weight = 0
301
+
302
+ [[source]]
303
+ id = "enterprise"
304
+ name = "Android Enterprise (Work)"
305
+ category = "guides"
306
+ enabled = true
307
+ detector = "android_sitemap"
308
+ url = "https://developer.android.com/work"
309
+ path_prefix = "/work"
310
+ feed_url = ""
311
+ content_selector = ""
312
+ default_weight = 0
313
+
314
+ [[source]]
315
+ id = "ai"
316
+ name = "AI on Android (Gemini / on-device ML)"
317
+ category = "guides"
318
+ enabled = true
319
+ detector = "android_sitemap"
320
+ url = "https://developer.android.com/ai"
321
+ path_prefix = "/ai"
322
+ feed_url = ""
323
+ content_selector = ""
324
+ default_weight = 0
325
+
326
+ [[source]]
327
+ id = "kotlin"
328
+ name = "Kotlin on Android"
329
+ category = "guides"
330
+ enabled = true
331
+ detector = "android_sitemap"
332
+ url = "https://developer.android.com/kotlin"
333
+ path_prefix = "/kotlin"
334
+ feed_url = ""
335
+ content_selector = ""
336
+ default_weight = 0
337
+
338
+ [[source]]
339
+ id = "privacy-security"
340
+ name = "Privacy & security"
341
+ category = "guides"
342
+ enabled = true
343
+ detector = "android_sitemap"
344
+ url = "https://developer.android.com/privacy-and-security"
345
+ path_prefix = "/privacy-and-security"
346
+ feed_url = ""
347
+ content_selector = ""
348
+ default_weight = 0
349
+
350
+ [[source]]
351
+ id = "quality"
352
+ name = "Core App Quality / vitals"
353
+ category = "guides"
354
+ enabled = true
355
+ detector = "android_sitemap"
356
+ url = "https://developer.android.com/quality"
357
+ path_prefix = "/quality"
358
+ feed_url = ""
359
+ content_selector = ""
360
+ default_weight = 0
361
+
362
+ [[source]]
363
+ id = "distribute"
364
+ name = "Distribution hub"
365
+ category = "guides"
366
+ enabled = true
367
+ detector = "android_sitemap"
368
+ url = "https://developer.android.com/distribute"
369
+ path_prefix = "/distribute"
370
+ feed_url = ""
371
+ content_selector = ""
372
+ default_weight = 0
373
+
374
+ [[source]]
375
+ id = "play-services-releases"
376
+ name = "Play services release notes"
377
+ category = "api-reference"
378
+ enabled = true
379
+ detector = "content"
380
+ url = "https://developers.google.com/android/guides/releases"
381
+ path_prefix = ""
382
+ feed_url = ""
383
+ content_selector = ""
384
+ default_weight = 0
385
+
386
+ [[source]]
387
+ id = "play-billing"
388
+ name = "Play Billing"
389
+ category = "guides"
390
+ enabled = true
391
+ detector = "android_sitemap"
392
+ url = "https://developer.android.com/google/play/billing"
393
+ path_prefix = "/google/play/billing"
394
+ feed_url = ""
395
+ content_selector = ""
396
+ default_weight = 0
397
+
398
+ # androidx-per-library watches all AndroidX release notes under
399
+ # /jetpack/androidx/releases; compose-releases (a longer prefix) carves out its
400
+ # own subtree via most-specific-prefix matching, so neither double-reports.
401
+ [[source]]
402
+ id = "androidx-per-library"
403
+ name = "AndroidX per-library release notes"
404
+ category = "api-reference"
405
+ enabled = true
406
+ detector = "android_sitemap"
407
+ url = "https://developer.android.com/jetpack/androidx/releases"
408
+ path_prefix = "/jetpack/androidx/releases"
409
+ feed_url = ""
410
+ content_selector = ""
411
+ default_weight = 0
412
+
413
+ # Broad documentation sections: each watches a whole top-level area for edits.
414
+ # Narrower release-notes sources nested under these (e.g. /studio/releases,
415
+ # /tools/releases/platform-tools, /ndk/downloads/revision_history) carve out
416
+ # their own subtrees via most-specific-prefix matching, so neither double-reports.
417
+ [[source]]
418
+ id = "develop-docs"
419
+ name = "Develop guides"
420
+ category = "guides"
421
+ enabled = true
422
+ detector = "android_sitemap"
423
+ url = "https://developer.android.com/develop"
424
+ path_prefix = "/develop"
425
+ feed_url = ""
426
+ content_selector = ""
427
+ default_weight = 0
428
+
429
+ [[source]]
430
+ id = "topic-hubs"
431
+ name = "Topic hubs"
432
+ category = "guides"
433
+ enabled = true
434
+ detector = "android_sitemap"
435
+ url = "https://developer.android.com/topic"
436
+ path_prefix = "/topic"
437
+ feed_url = ""
438
+ content_selector = ""
439
+ default_weight = 0
440
+
441
+ [[source]]
442
+ id = "design-guidance"
443
+ name = "Design guidance"
444
+ category = "design"
445
+ enabled = true
446
+ detector = "android_sitemap"
447
+ url = "https://developer.android.com/design"
448
+ path_prefix = "/design"
449
+ feed_url = ""
450
+ content_selector = ""
451
+ default_weight = 0
452
+
453
+ [[source]]
454
+ id = "android-studio-docs"
455
+ name = "Android Studio docs"
456
+ category = "tooling"
457
+ enabled = true
458
+ detector = "android_sitemap"
459
+ url = "https://developer.android.com/studio"
460
+ path_prefix = "/studio"
461
+ feed_url = ""
462
+ content_selector = ""
463
+ default_weight = 0
464
+
465
+ [[source]]
466
+ id = "ndk-docs"
467
+ name = "NDK docs"
468
+ category = "tooling"
469
+ enabled = true
470
+ detector = "android_sitemap"
471
+ url = "https://developer.android.com/ndk"
472
+ path_prefix = "/ndk"
473
+ feed_url = ""
474
+ content_selector = ""
475
+ default_weight = 0
476
+
477
+ [[source]]
478
+ id = "sdk-tools-docs"
479
+ name = "SDK tools docs"
480
+ category = "tooling"
481
+ enabled = true
482
+ detector = "android_sitemap"
483
+ url = "https://developer.android.com/tools"
484
+ path_prefix = "/tools"
485
+ feed_url = ""
486
+ content_selector = ""
487
+ default_weight = 0
488
+
489
+ # Comprehensive host catch-alls. Each watches a whole host's sitemap; the curated
490
+ # sources above carve out their subtrees via most-specific-prefix matching, so
491
+ # nothing double-reports. Reference docs are filtered (index/summary only, or
492
+ # dropped); /sdk is dropped (redirects to /studio); per-symbol class pages go.
493
+ [[source]]
494
+ id = "developer-android-all"
495
+ name = "developer.android.com (all)"
496
+ category = "guides"
497
+ enabled = true
498
+ detector = "android_sitemap"
499
+ url = "https://developer.android.com/"
500
+ path_prefix = ""
501
+ feed_url = ""
502
+ content_selector = ""
503
+ default_weight = 0
504
+ exclude_prefixes = ["/sdk"]
505
+ reference_mode = "index_only"
506
+
507
+ [[source]]
508
+ id = "developers-google-android"
509
+ name = "developers.google.com Android docs"
510
+ category = "guides"
511
+ enabled = true
512
+ detector = "android_sitemap"
513
+ url = "https://developers.google.com/"
514
+ path_prefix = ""
515
+ feed_url = ""
516
+ content_selector = ""
517
+ default_weight = 0
518
+ require_segment = "android"
519
+ reference_mode = "drop"
520
+
521
+ [[source]]
522
+ id = "kotlin-lang"
523
+ name = "Kotlin language docs"
524
+ category = "guides"
525
+ enabled = true
526
+ detector = "android_sitemap"
527
+ url = "https://kotlinlang.org/"
528
+ path_prefix = ""
529
+ feed_url = ""
530
+ content_selector = ""
531
+ default_weight = 0