synd-term 0.3.0

terminal feed viewer
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
<div class="oranda-hide">

# Syndicationd

</div>

[![CI][workflow-ci-badge]][workflow-ci-url]
[![Release][workflow-release-badge]][workflow-release-url]
[![Coverage][coverage-badge]][coverage-url]

[crates-badge]: https://img.shields.io/crates/v/synd-term?style=for-the-badge&logo=rust
[crates-url]: https://crates.io/crates/synd-term
[license-badge]: https://img.shields.io/badge/license-MIT%2FApache--2.0-blue?style=for-the-badge
[workflow-ci-badge]: https://img.shields.io/github/actions/workflow/status/ymgyt/syndicationd/ci.yaml?style=for-the-badge&logo=github&label=CI
[workflow-ci-url]: https://github.com/ymgyt/syndicationd/actions/workflows/ci.yaml
[workflow-release-badge]: https://img.shields.io/github/actions/workflow/status/ymgyt/syndicationd/release.yml?style=for-the-badge&logo=github&label=Release
[workflow-release-url]: https://github.com/ymgyt/syndicationd/actions/workflows/release.yml
[coverage-badge]: https://img.shields.io/codecov/c/github/ymgyt/syndicationd?token=W1A93WSPEE&style=for-the-badge&logo=codecov&color=brightgreen
[coverage-url]: https://app.codecov.io/github/ymgyt/syndicationd
[grafana-badge]: https://img.shields.io/badge/Grafana-Dashboard-orange?style=for-the-badge&logo=grafana
[grafana-url]: https://ymgyt.grafana.net/public-dashboards/863ebddd82c44ddd9a28a68eaac848ff?orgId=1&refresh=1h&from=now-1h&to=now

![Demo](./assets/demo.gif)

Syndicationd(`synd`) is a TUI feed viewer, based on [feed-rs](https://github.com/feed-rs/feed-rs) and [ratatui](https://github.com/ratatui-org/ratatui).

**Table of Contents:**

- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
  - [Authentication](#authentication)
  - [Keymap](#keymap)
  - [Subscribe Feed](#subscribe-feed)
  - [Export Feeds](#export-subscribed-feeds)
  - [GitHub Notifications](#github-notifications)
  - [Theme](#theme)
  - [Backend API](#backend-api)
  - [Log File](#log-file)
  - [Clean](#remove-cache-and-logs)
- [License](#license)

## Features

* Subscribe RSS/Atom feeds
* Handle [GitHub notifications](https://github.com/notifications)

## Installation

### nix

```sh
nix profile install github:ymgyt/syndicationd/synd-term-v0.3.0
```

### arch linux

```sh
pacman -S syndicationd
```

### brew

```sh
brew install ymgyt/homebrew-syndicationd/synd-term
```

### shell

```sh
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ymgyt/syndicationd/releases/download/synd-term-v0.3.0
```

### cargo

```sh
cargo install synd-term --locked
```

### npm

```sh
npm install @syndicationd/synd-term
```

### powershell

```sh
powershell -c "irm https://github.com/ymgyt/syndicationd/releases/download/synd-term-v0.3.0
```

### docker

```sh
docker run -it ghcr.io/ymgyt/synd-term
```

### pre-build binaries

Pre-built binaries are available in [GitHub releases](https://github.com/ymgyt/syndicationd/releases).


## Usage

`synd` will start the TUI application.

<details>
<summary>Click to show a complete list of options</summary>

```sh
Usage:

Commands:
  clean   Clean cache and logs
  check   Check application conditions
  export  Export subscribed feeds
  help    Print this message or the help of the given subcommand(s)

Options:
      --log <LOG>              Log file path [env: SYND_LOG_PATH=] [default:
                               /home/ymgyt/.local/share/synd/synd.log]
      --cache-dir <CACHE_DIR>  Cache directory [default: /home/ymgyt/.cache/synd]
      --theme <THEME>          Color theme [env: SYND_THEME=] [default: ferra] [possible values: ferra,
                               solarized-dark, helix]
  -h, --help                   Print help
  -V, --version                Print version

Api options:
      --endpoint <ENDPOINT>              `synd_api` endpoint [env: SYND_ENDPOINT=] [default:
                                         https://api.syndicationd.ymgyt.io:6100]
      --client-timeout <CLIENT_TIMEOUT>  Client timeout [default: 30s]

Feed options:
      --categories <CATEGORIES TOML PATH>  categories.toml path
      --entries-limit <ENTRIES_LIMIT>      Feed entries limit to fetch [default: 200]

GitHub options:
  -G, --enable-github-notification  Enable GitHub notification feature [env: SYND_ENABLE_GH=]
                                    [aliases: enable-gh]
      --github-pat <GITHUB_PAT>     GitHub personal access token to fetch notifications [env:
                                    SYND_GH_PAT]
```

</details>

### Authentication

syndicationd maintains state (such as subscribed feeds) on the backend, and therefore requires authentication to make requests.  
Currently, GitHub and Google are supported as authorize server/id provider. The only scope syndicationd requires is [`user:email`](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps)(Github) or [`email`](https://developers.google.com/identity/gsi/web/guides/devices#obtain_a_user_code_and_verification_url)(Google) to authenticate the user's email address. The user's email address is used only as an identifier after being hashed and never stored. for more info, please refer to the [privacy policy](https://docs.syndicationd.ymgyt.io/synd-term/book/privacy_policy.html)

### Keymap

<details>
<summary>Click to show a keymap table</summary>

| Key     | Description                                   |
| ---     | ---                                           |
| `k/j`   | Move up/down                                  |
| `gg`    | Go to first                                   |
| `ge`    | Go to end                                     |
| `Tab`   | Switch Tab                                    |
| `Enter` | Open entry/feed                               |
| `a`     | Add feed subscription(on Feeds Tab)           |
| `e`     | Edit subscribed feed(on Feeds Tab)            |
| `d`     | Delete subscribed feed(on Feeds Tab)          |
| `r`     | Reload entries/feeds                          |
| `h/l`   | Change requirement filiter                    |
| `c`     | Activate category filiter(Esc to deactivate)  |
| `+`     | Activate all category(on Category filter)     |
| `-`     | Deactivate all category(on Category filter)   |
| `/`     | Activate keyword search(Esc to deactivate)    |
| `q`     | Quit app                                      |

#### GitHub Notification

| Key     | Description                                   |
| ---     | ---                                           |
| `d`     | Mark as done                                  |
| `D`     | Mark all as done                              |
| `u`     | Unsubscribe                                   |
| `f`     | Open notification filter(Esc to apply)        |

</details>

for more details, refer to [`keymap/default.rs`](https://github.com/ymgyt/syndicationd/blob/main/crates/synd_term/src/keymap/default.rs)

### Subscribe feed

To subscribe a feed, type "Tab" to move to Feeds tab and then press "a".  
`synd` uses [edit](https://docs.rs/edit/latest/edit/) to launch the user's editor(like a git commit).  
The feed to subscribe to should be entered in the format:  
`Requirement` `Category` `URL`  

When you close the editor, the subscription request is sent to the API.

#### Requirement

`Requirement` indicates the importance of the feed.  
This uses an analogy to [RFC2119](https://datatracker.ietf.org/doc/html/rfc2119) and can take one of the following values:

* `MUST`: Most important, must be read.
* `SHOULD`: Next in importance, should be read unless there is a special reason not to.
* `MAY`: Lowest importance, may be read.

#### Category

`Category` represents the category of the feed. You can specify any value as a category. The values that `synd` recognizes as categories are defined in [`categories.toml`](./categories.toml). You can override the default values with the `--categories` flag.


### Edit subscribed feed

To change the requirement or category of a feed you have already subscribed to, select the target feed in the Feeds tab and then press "e".

### Unsubscribe feed

To unsubscribe from a feed, select the target feed and press "d".

### Filter feeds/entries

Feeds and entries can be filtered as follows.

#### By requirement

To filter bases on the specified requirement, press "h/l(Left/Right)".  
If you set the filter to `MUST`, only those marked as MUST will be displayed. Setting it to SHOULD will display feeds and entries marked as MUST and SHOULD. If set to MAY, all feeds and entries will be displayed.

#### By categories

To filter bases on categories, presess "c". This will display a label with keys to control the activation/deactivation of each category, allowing you to toggle the visibility of categories.  
Pressing "-" will deactivate all categories, and pressing "+" will activate all categories.  

You can exit the filter category mode by pressing the "Esc" key.  
The icons for categories can be specified in `categories.toml`


### Export subscribed feeds

To export subscribed feeds, execute the `synd export` command.  
You can check the JSON schema of the data to be exported with `synd export --print-schema`

<details>
<summary>Click to show a export json schema</summary>

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Export",
  "type": "object",
  "required": [
    "feeds"
  ],
  "properties": {
    "feeds": {
      "type": "array",
      "items": {
        "$ref": "#/definitions/ExportedFeed"
      }
    }
  },
  "definitions": {
    "ExportedFeed": {
      "type": "object",
      "required": [
        "url"
      ],
      "properties": {
        "title": {
          "type": [
            "string",
            "null"
          ]
        },
        "url": {
          "type": "string"
        }
      }
    }
  }
}
```
</details>

### GitHub Notifications

To enable GitHub notifications feature, specify the `--enable-github-notification | -G` flag or set the environment variable `SYND_ENABLE_GH=true`.  
When enabling the GitHub notifications feature, GitHub personal access token (PAT) is required. Specify the PAT using the `--github-pat` flag or the environment variable `SYND_GH_PAT`.  
For GitHub notifications, unlike feeds, the synd-api is not used.

#### PAT Scope

##### Classic token

The `repo` scope is required. For more details, see [about github notifications](https://docs.github.com/en/rest/activity/notifications?apiVersion=2022-11-28#about-github-notifications).

##### Fine grained access token

"Metadata" repository permissions (read) and "Notifications" user permissions (read) are required.  
For more details, see [list notifications for the authenticated user](https://docs.github.com/en/rest/activity/notifications?apiVersion=2022-11-28#list-notifications-for-the-authenticated-user).  
Since the Mark notification as done API does not support fine grained access token, c class token is required to use this feature.  


### Log file

The log file path is based on [`ProjectDirs::data_dir()`](https://docs.rs/directories/latest/directories/struct.ProjectDirs.html#method.data_dir).  
Please refer to the `--log` flag in `synd --help` for the default output destination.  

You can modify the [log directives](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives) using the environment variable `SYND_LOG`. (for example, `SYND_LOG=synd=debug`)

### Theme

The theme can be changed using the `--theme` flag. Please refer to the help for the values that can be specified.

### Backend api

By default, `synd` use `https://api.syndicationd.ymgyt.io` as the [backend api](./crates/synd_api)([hosted on my home Raspberry Pi](https://github.com/ymgyt/mynix/blob/main/homeserver/modules/syndicationd/default.nix)).  
To change the endpoint, specify the `--endpoint` flag

The hosted api is instrumented with OpenTelemetry. Basic signals(traces,metrics,logs) are published on the [![Grafana][grafana-badge]][grafana-url]


### Remove cache and logs

Authentication credentials are cached. to remove them, execute `synd clean`.

### Check application status

`synd check [--format (human|json)]` return current application status.

```sh
synd check --format json | from json
╭───────┬─────────────────────────────────────────╮
│       │ ╭─────────────┬────────────────────╮    │
│ api   │ │ description │ health of synd-api │    │
│       │ │ status      │ Pass               │    │
│       │ │ version     │ 0.1.9              │    │
│       │ ╰─────────────┴────────────────────╯    │
│ cache │ /home/ferris/.cache/synd                │
│ log   │ /home/ferris/.local/share/synd/synd.log │
╰───────┴─────────────────────────────────────────╯
```

## License

This project is available under the terms of either the [Apache 2.0 license](./LICENSE-APACHE) or the [MIT license](./LICENSE-MIT).

## Feed tips

Some tips about feed that I know.

* Add [`openrss.org/`](https://openrss.org/) to the beginning of the URL to get its RSS feed. for example, for `https://example.ymgyt.io`, it would be `https://openrss.org/example.ymgyt.io`

* You can retrieve various updates as feeds on GitHub.
  * To obtain releases of a repository, specify `releases.atom`. for example, to obtain releases of syndicationd, specify `https://github.com/ymgyt/syndicationd/releases.atom`
  * For tags, it's `https://github.com/ymgyt/syndicationd/tag.atom` 

* Adding `.rss` to the end of a Reddit URL allows you to retrieve the feed. for example, for `https://www.reddit.com/r/HelixEditor/`, it would be `https://www.reddit.com/r/HelixEditor.rss`

<details>
<summary>Click to show more tips</summary>
                       
* Adding `.ja.atom` to the end of a connpass group URL to get the event feed. for example, `https://opentelemetry.connpass.com/ja.atom` 

* Adding `/feed` for Hatena blog
</details>

If you're looking for feeds, here are my recommendations.

<details>
<summary>Click to show a list</summary>

### For Rust users

<details>
<summary>Click to show a table</summary>

| Feed | URL |
| ---  | --- |
| [This Week in Rust](https://this-week-in-rust.org/) | `https://this-week-in-rust.org/atom.xml`|
| [Rust Blog](https://without.boats/index.xml) | `https://blog.rust-lang.org/feed.xml` |
| [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/) | `https://blog.rust-lang.org/inside-rust/feed.xml` |
| [RustSec Advisories](https://rustsec.org/) | `https://rustsec.org/feed.xml` |
| [seanmonstar](https://seanmonstar.com/) | `https://seanmonstar.com/rss` |
| [Mara's Blog](https://blog.m-ou.se/) | `https://blog.m-ou.se/index.xml` |
| [Without boats, dreams dry up](https://without.boats/) | `https://without.boats/index.xml` |
| [fasterthanli.me](https://fasterthanli.me/) | `https://fasterthanli.me/index.xml` |
| [Orhun's Blog](https://blog.orhun.dev/) | `https://blog.orhun.dev/rss.xml` |
| [axo blog](https://blog.axo.dev/) | `https://blog.axo.dev/rss.xml` |
| [Kbzol's blog](https://kobzol.github.io/) | `https://kobzol.github.io/feed.xml` |
| [baby steps](https://smallcultfollowing.com/babysteps/) | `https://smallcultfollowing.com/babysteps/` |
| [COCl2's blog home](https://blog.cocl2.com/) | `https://blog.cocl2.com/index.xml` |

</details>

### For Nix users

<details>
<summary>Click to show a table</summary>

| Feed | URL |
| ---  | --- |
| [This Cute World](https://thiscute.world/en/) | `https://thiscute.world/en/index.xml` |
| [Determinate Systems](https://determinate.systems/) | `https://determinate.systems/rss.xml` |
</details>


### For Observability 

<details>
<summary>Click to show a table</summary>

| Feed | URL |
| ---  | --- |
| [observability news](https://buttondown.email/o11y.news) | `https://buttondown.email/o11y.news/rss` |
| [Opentelemetry blog](https://opentelemetry.io/blog/2024/) | `https://opentelemetry.io/blog/2024/index.xml` |
| [eBPF Blog](https://ebpf.io/blog/) | `https://ebpf.io/blog/rss.xml` |

</details>

### For Kubernetes users

<details>
<summary>Click to show a table</summary>

| Feed | URL |
| ---  | --- |
| [Kubernetes Blog](https://kubernetes.io/) | ` https://kubernetes.io/feed.xml` |
| [Kubernetes Official CVE](https://kubernetes.io/docs/reference/issues-security/official-cve-feed/) | `https://kubernetes.io/docs/reference/issues-security/official-cve-feed/feed.xml` |
| [CNCF](https://www.cncf.io/) | `https://www.cncf.io/feed/` |
</details>

### Misc

<details>
<summary>Click to show a table</summary>

| Feed | URL |
| ---  | --- |
| [Terminal Trove](https://terminaltrove.com/) | `https://terminaltrove.com/blog.xml` |

</details>
</details>