Skip to content

Commit fdf079f

Browse files
RydalWateryukibtc
authored andcommitted
book: add filters python examples
* Minor correction of typo that is finding its way into the Rust Docs. * Create filters * Modify Filters * Other Filter operations * Correction to nip05.py to remove unused classes from import Closes #463 Signed-off-by: Yuki Kishimoto <[email protected]>
1 parent f9010ee commit fdf079f

File tree

6 files changed

+362
-3
lines changed

6 files changed

+362
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
### Added
3434

35+
* book: add some python examples ([RydalWater])
36+
3537
### Fixed
3638

3739
### Removed

book/snippets/nostr/python/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import asyncio
44

55
from src.keys import generate, restore, vanity
6+
from src.filters import filters
67
from src.event.json import event_json
78
from src.event.builder import event_builder
89
from src.nip01 import nip01
@@ -21,6 +22,7 @@ async def main():
2122
vanity()
2223
event_json()
2324
event_builder()
25+
filters()
2426
nip01()
2527
await nip05()
2628
nip06()
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
from nostr_protocol import Filter, FilterRecord, Keys, Kind, EventBuilder, Timestamp, Tag
2+
import time, datetime
3+
4+
def filters():
5+
# Generate keys and Events
6+
keys = Keys.generate()
7+
keys2 = Keys.generate()
8+
event = EventBuilder.text_note("Hello World!",[]).to_event(keys)
9+
event2 = EventBuilder(Kind(1),"Goodbye World!", [Tag.identifier("Identification D Tag")]).to_event(keys2)
10+
11+
print()
12+
print("Creating Filters:")
13+
14+
# ANCHOR: create-filter-id
15+
# Filter for specific ID
16+
print(" Filter for specific Event ID:")
17+
f = Filter().id(event.id())
18+
print(f" {f.as_json()}")
19+
# ANCHOR_END: create-filter-id
20+
21+
print()
22+
# ANCHOR: create-filter-author
23+
# Filter for specific Author
24+
print(" Filter for specific Author:")
25+
f = Filter().author(keys.public_key())
26+
print(f" {f.as_json()}")
27+
# ANCHOR_END: create-filter-author
28+
29+
print()
30+
# ANCHOR: create-filter-kind-pk
31+
# Filter by PK and Kinds
32+
print(" Filter with PK and Kinds:")
33+
f = Filter()\
34+
.pubkey(keys.public_key())\
35+
.kind(Kind(1))
36+
print(f" {f.as_json()}")
37+
# ANCHOR_END: create-filter-kind-pk
38+
39+
print()
40+
# ANCHOR: create-filter-search
41+
# Filter for specific string
42+
print(" Filter for specific search string:")
43+
f = Filter().search("Ask Nostr Anything")
44+
print(f" {f.as_json()}")
45+
# ANCHOR_END: create-filter-search
46+
47+
print()
48+
# ANCHOR: create-filter-timeframe
49+
print(" Filter for events from specific public key within given timeframe:")
50+
# Create timestamps
51+
date = datetime.datetime(2009, 1, 3, 0, 0)
52+
timestamp = int(time.mktime(date.timetuple()))
53+
since_ts = Timestamp.from_secs(timestamp)
54+
until_ts = Timestamp.now()
55+
56+
# Filter with timeframe
57+
f = Filter()\
58+
.pubkey(keys.public_key())\
59+
.since(since_ts)\
60+
.until(until_ts)
61+
print(f" {f.as_json()}")
62+
# ANCHOR_END: create-filter-timeframe
63+
64+
print()
65+
# ANCHOR: create-filter-limit
66+
# Filter for specific PK with limit
67+
print(" Filter for specific Author, limited to 10 Events:")
68+
f = Filter()\
69+
.author(keys.public_key())\
70+
.limit(10)
71+
print(f" {f.as_json()}")
72+
# ANCHOR_END: create-filter-limit
73+
74+
print()
75+
# ANCHOR: create-filter-hashtag
76+
# Filter for Hashtags
77+
print(" Filter for a list of Hashtags:")
78+
f = Filter().hashtags(["#Bitcoin", "#AskNostr", "#Meme"])
79+
print(f" {f.as_json()}")
80+
# ANCHOR_END: create-filter-hashtag
81+
82+
print()
83+
# ANCHOR: create-filter-reference
84+
# Filter for Reference
85+
print(" Filter for a Reference:")
86+
f = Filter().reference("This is my NIP-12 Reference")
87+
print(f" {f.as_json()}")
88+
# ANCHOR_END: create-filter-reference
89+
90+
print()
91+
# ANCHOR: create-filter-identifier
92+
# Filter for Identifier
93+
print(" Filter for a Identifier:")
94+
f = Filter().identifier(event2.identifier())
95+
print(f" {f.as_json()}")
96+
# ANCHOR_END: create-filter-identifier
97+
98+
print()
99+
print("Modifying Filters:")
100+
# ANCHOR: modify-filter
101+
# Modifying Filters (adding/removing)
102+
f = Filter()\
103+
.pubkeys([keys.public_key(), keys2.public_key()])\
104+
.ids([event.id(), event2.id()])\
105+
.kinds([Kind(0), Kind(1)])\
106+
.author(keys.public_key())
107+
108+
# Add an additional Kind to existing filter
109+
f = f.kinds([Kind(4)])
110+
111+
# Print Results
112+
print(" Before:")
113+
print(f" {f.as_json()}")
114+
print()
115+
116+
# Remove PKs, Kinds and IDs from filter
117+
f = f.remove_pubkeys([keys2.public_key()])
118+
print(" After (remove pubkeys):")
119+
print(f" {f.as_json()}")
120+
121+
f = f.remove_kinds([Kind(0), Kind(4)])
122+
print(" After (remove kinds):")
123+
print(f" {f.as_json()}")
124+
125+
f = f.remove_ids([event2.id()])
126+
print(" After (remove IDs):")
127+
print(f" {f.as_json()}")
128+
# ANCHOR_END: modify-filter
129+
130+
print()
131+
print("Other Filter Operations:")
132+
# ANCHOR: other-parse
133+
# Parse filter
134+
print(" Parse Filter from Json:")
135+
f_json = f.as_json()
136+
f = Filter().from_json(f_json)
137+
print(f" {f.as_record()}")
138+
# ANCHOR_END: other-parse
139+
140+
print()
141+
# ANCHOR: other-record
142+
print(" Construct Filter Record and extract author:")
143+
# Filter Record
144+
fr = FilterRecord(ids=[event.id()],authors=[keys.public_key()], kinds=[Kind(0)], search="", since=None, until=None, limit=1, generic_tags=[])
145+
f = Filter().from_record(fr)
146+
print(f" {f.as_json()}")
147+
# ANCHOR_END: other-record
148+
149+
print()
150+
# ANCHOR: other-match
151+
print(" Logical tests:")
152+
f = Filter().author(keys.public_key()).kind(Kind(1))
153+
print(f" Event match for filter: {f.match_event(event)}")
154+
print(f" Event2 match for filter: {f.match_event(event2)}")
155+
# ANCHOR_END: other-match

book/snippets/nostr/python/src/nip05.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from nostr_protocol import Keys, Metadata, EventBuilder, PublicKey, verify_nip05, get_nip05_profile
1+
from nostr_protocol import Metadata, PublicKey, verify_nip05, get_nip05_profile
22

33

44
async def nip05():

book/src/nostr/05_01_01-filter.md

Lines changed: 201 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,201 @@
1-
# Filter
1+
# Filters
2+
3+
Though a web-socket subscription model relays can surface events that meet specific criteria on request.
4+
The means by which these requests maybe submitted are JSON filters objects which can be constructed using a range of attributes,
5+
including `ids`, `authors`, `kinds` and single letter `tags`, along with timestamps, `since`/`until` and record `limit` for the query.
6+
7+
The [Filters](https://docs.rs/nostr/latest/nostr/event/builder/struct.Filters.html) struct allows us to create and modify filter objects.
8+
The available functions allow for both single (`author`) and multiple (`authors`) items to be added during creation/addition,
9+
and the attribute specific remove functions (`remove_authors`) can be used to modify existing filter objects.
10+
There are also some additional functions which cover logical tests namely `is_empty()` and `match_event()`.
11+
12+
## Create Filters
13+
14+
<custom-tabs category="lang">
15+
16+
<div slot="title">Rust</div>
17+
<section>
18+
19+
TODO
20+
21+
</section>
22+
23+
<div slot="title">Python</div>
24+
<section>
25+
26+
The following code examples all utilise the `Filters()` along with associated methods to create filter objects and print these in JSON format using the `as_json()` method.
27+
28+
Filtering events based on a specific event ID using `id()`.
29+
30+
```python,ignore
31+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-id}}
32+
```
33+
34+
Filtering events by author using `author()`.
35+
36+
```python,ignore
37+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-author}}
38+
```
39+
40+
Filtering events based on multiple criteria. In this case, by public key using `public_key()` and kind using `kind()`.
41+
42+
```python,ignore
43+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-kind-pk}}
44+
```
45+
46+
Filtering for specific text strings using `search()`.
47+
48+
```python,ignore
49+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-search}}
50+
```
51+
52+
Restricting query results to specific timeframes (using `since()` and `until()`), as well as limiting search results to a maximum of 10 records using `limit()`.
53+
54+
```python,ignore
55+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-timeframe}}
56+
```
57+
58+
```python,ignore
59+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-limit}}
60+
```
61+
62+
Finally, filtering using hashtags (`hashtag()`), NIP-12 reference tags (`reference()`) and identifiers (`identifiers()`), respectively.
63+
64+
```python,ignore
65+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-generic-tag}}
66+
```
67+
68+
```python,ignore
69+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-reference}}
70+
```
71+
72+
```python,ignore
73+
{{#include ../../snippets/nostr/python/src/filters.py:create-filter-identifier}}
74+
```
75+
76+
</section>
77+
78+
<div slot="title">JavaScript</div>
79+
<section>
80+
81+
TODO
82+
83+
</section>
84+
85+
<div slot="title">Kotlin</div>
86+
<section>
87+
88+
TODO
89+
90+
</section>
91+
92+
<div slot="title">Swift</div>
93+
<section>
94+
95+
TODO
96+
97+
</section>
98+
</custom-tabs>
99+
100+
## Modify Filters
101+
102+
<custom-tabs category="lang">
103+
104+
<div slot="title">Rust</div>
105+
<section>
106+
107+
TODO
108+
109+
</section>
110+
111+
<div slot="title">Python</div>
112+
<section>
113+
114+
Adding more conditions to existing objects can be done by simply calling the relevant method on the instance of the object.
115+
In this example we create a initial filter with `pubkeys()`, `ids()`, `kinds()` and a single `author()` then modify the object further to include another kind (4) to the existing list of kinds (0, 1).
116+
117+
Similarly, the range of 'remove' methods (e.g. `remove_kinds()`) allow us to take an existing filter and remove unwanted conditions without needed to reconstruct the filter object from scratch.
118+
119+
```python,ignore
120+
{{#include ../../snippets/nostr/python/src/filters.py:modify-filter}}
121+
```
122+
123+
</section>
124+
125+
<div slot="title">JavaScript</div>
126+
<section>
127+
128+
TODO
129+
130+
</section>
131+
132+
<div slot="title">Kotlin</div>
133+
<section>
134+
135+
TODO
136+
137+
</section>
138+
139+
<div slot="title">Swift</div>
140+
<section>
141+
142+
TODO
143+
144+
</section>
145+
</custom-tabs>
146+
147+
## Other Filter Operations
148+
149+
<custom-tabs category="lang">
150+
151+
<div slot="title">Rust</div>
152+
<section>
153+
154+
TODO
155+
156+
</section>
157+
158+
<div slot="title">Python</div>
159+
<section>
160+
161+
We can parse existing filter JSON object using the `from_json()` method when instantiating a filter object.
162+
163+
```python,ignore
164+
{{#include ../../snippets/nostr/python/src/filters.py:other-parse}}
165+
```
166+
167+
Furthermore, it is possible to create filter records more formally using the `FilterRecord` class.
168+
169+
```python,ignore
170+
{{#include ../../snippets/nostr/python/src/filters.py:other-record}}
171+
```
172+
173+
To perform a logical test and determine if a given event object matches existing filter conditions the `match_event()` method can be used.
174+
175+
```python,ignore
176+
{{#include ../../snippets/nostr/python/src/filters.py:other-match}}
177+
```
178+
179+
</section>
180+
181+
<div slot="title">JavaScript</div>
182+
<section>
183+
184+
TODO
185+
186+
</section>
187+
188+
<div slot="title">Kotlin</div>
189+
<section>
190+
191+
TODO
192+
193+
</section>
194+
195+
<div slot="title">Swift</div>
196+
<section>
197+
198+
TODO
199+
200+
</section>
201+
</custom-tabs>

0 commit comments

Comments
 (0)