1# Drinkup
2
3An Elixir library for listening to events from an ATProtocol relay
4(firehose/`com.atproto.sync.subscribeRepos`). Eventually aiming to support any
5ATProtocol subscription.
6
7## TODO
8
9- Support for different subscriptions other than
10 `com.atproto.sync.subscribeRepo'.
11- Validation (signatures, making sure to only track handle active accounts,
12 etc.) (see
13 [Firehose Validation Best Practices](https://atproto.com/specs/sync#firehose-validation-best-practices))
14- Look into backfilling? See if there's better ways to do it.
15- Built-in solutions for tracking resumption? (probably a pluggable solution to
16 allow for different things like Mnesia, Postgres, etc.)
17- Testing of multi-node/distribution.
18- Tests
19- Documentation
20
21## Installation
22
23Add `drinkup` to your `mix.exs`.
24
25```elixir
26def deps do
27 [
28 {:drinkup, "~> 0.1"}
29 ]
30end
31```
32
33Documentation can be found on HexDocs at https://hexdocs.pm/drinkup.
34
35## Example Usage
36
37First, create a module implementing the `Drinkup.Consumer` behaviour (only
38requires a `handle_event/1` function):
39
40```elixir
41defmodule ExampleConsumer do
42 @behaviour Drinkup.Consumer
43
44 def handle_event(%Drinkup.Event.Commit{} = event) do
45 IO.inspect(event, label: "Got commit event")
46 end
47
48 def handle_event(_), do: :noop
49end
50```
51
52Then add Drinkup and your consumer to your application's supervision tree:
53
54```elixir
55defmodule MyApp.Application do
56 use Application
57
58 def start(_type, _args) do
59 children = [{Drinkup, %{consumer: ExampleConsumer}}]
60 Supervisor.start_link(children, strategy: :one_for_one)
61 end
62end
63```
64
65You should then be able to start your application and start seeing
66`Got commit event: ...` in the terminal.
67
68### Record Consumer
69
70One of the main reasons for listening to an ATProto relay is to synchronise a
71database with records. As a result, Drinkup provides a light extension around a
72basic consumer, the `RecordConsumer`, which only listens to commit events, and
73transforms them into a slightly nicer structure to work around, calling your
74`handle_create/1`, `handle_update/1`, and `handle_delete/1` functions for each
75record it comes across. It also allows for filtering of specific types of
76records either by full name or with a
77[Regex](https://hexdocs.pm/elixir/1.18.4/Regex.html) match.
78
79```elixir
80defmodule ExampleRecordConsumer do
81 # Will respond to any events either `app.bsky.feed.post` records, or anything under `app.bsky.graph`.
82 use Drinkup.RecordConsumer, collections: [~r/app\.bsky\.graph\..+/, "app.bsky.feed.post"]
83 alias Drinkup.RecordConsumer.Record
84
85 def handle_create(%Record{type: "app.bsky.feed.post"} = record) do
86 IO.inspect(record, label: "Bluesky post created")
87 end
88
89 def handle_create(%Record{type: "app.bsky.graph" <> _} = record) do
90 IO.inspect(record, label: "Bluesky graph updated")
91 end
92
93 def handle_update(record) do
94 # ...
95 end
96
97 def handle_delete(record) do
98 # ...
99 end
100end
101```
102
103## Special thanks
104
105The process structure used in Drinkup is heavily inspired by the work done on
106[Nostrum](https://github.com/Kraigie/nostrum), an incredible Elixir library for
107Discord.
108
109## License
110
111This project is licensed under the [MIT License](./LICENSE)