+8
-4
.env.example
+8
-4
.env.example
···
1
-
JETSTREAM_URL=""
2
-
LABELER_URL=""
3
-
LABELER_KEY=""
4
-
REDIS_ADDR=""
1
+
PDS_URL="https://bsky.social"
2
+
ACCOUNT_HANDLE="your-handle.bsky.social"
3
+
ACCOUNT_PASSWORD="your-app-password"
4
+
WATCHED_OPS="did:plc:example1,did:plc:example2"
5
+
JETSTREAM_URL="wss://jetstream2.us-west.bsky.network/subscribe"
6
+
LABELER_URL="http://localhost:3000"
7
+
LABELER_KEY="your-secret-key-here"
8
+
LMSTUDIO_HOST="http://localhost:1234"
+7
LICENSE
+7
LICENSE
···
1
+
Copyright 2025 me@haileyok.com
2
+
3
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+182
README.md
+182
README.md
···
1
+
# dontshowmethis
2
+
3
+
An AI-powered Bluesky content moderation system that automatically labels replies to monitored accounts using LLM-based content analysis.
4
+
5
+
## Overview
6
+
7
+
This project monitors Bluesky posts in real-time via Jetstream and uses a local LLM (via LM Studio) to classify replies to watched accounts. It automatically applies labels such as "bad-faith", "off-topic", and "funny" to help users filter and moderate content.
8
+
9
+
The system consists of two components:
10
+
1. **Go Consumer** - Monitors the Jetstream firehose and analyzes replies using an LLM
11
+
2. **Skyware Labeler** - TypeScript server that manages and emits content labels
12
+
13
+
## Architecture
14
+
15
+
```
16
+
Jetstream → Go Consumer → LM Studio (LLM) → Labeler Service → Bluesky
17
+
```
18
+
19
+
1. The Go consumer subscribes to Jetstream and monitors replies to specified accounts
20
+
2. When a reply is detected, it fetches the parent post and sends both to LM Studio
21
+
3. The LLM classifies the reply based on the system prompt
22
+
4. Labels are emitted via the Skyware labeler service
23
+
5. Labels are propagated to Bluesky's labeling system
24
+
25
+
## Prerequisites
26
+
27
+
- [LM Studio](https://lmstudio.ai/) with a compatible model loaded
28
+
- A Bluesky account for the labeler
29
+
30
+
## Installation
31
+
32
+
### Clone the repository
33
+
34
+
```bash
35
+
git clone https://github.com/haileyok/dontshowmethis.git
36
+
cd dontshowmethis
37
+
```
38
+
39
+
### Install Go dependencies
40
+
41
+
```bash
42
+
go mod download
43
+
```
44
+
45
+
### Install labeler dependencies
46
+
47
+
```bash
48
+
cd labeler
49
+
yarn install
50
+
cd ..
51
+
```
52
+
53
+
## Configuration
54
+
55
+
Copy the example environment file and configure it:
56
+
57
+
```bash
58
+
cp .env.example .env
59
+
```
60
+
61
+
### Environment Variables
62
+
63
+
**For the Go Consumer:**
64
+
65
+
- `PDS_URL` - Your Bluesky PDS URL (e.g., `https://bsky.social`)
66
+
- `ACCOUNT_HANDLE` - Your Bluesky account handle
67
+
- `ACCOUNT_PASSWORD` - Your Bluesky account password
68
+
- `WATCHED_OPS` - Comma-separated list of DIDs to monitor for replies
69
+
- `JETSTREAM_URL` - Jetstream WebSocket URL (default: `wss://jetstream2.us-west.bsky.network/subscribe`)
70
+
- `LABELER_URL` - URL of your labeler service (e.g., `http://localhost:3000`)
71
+
- `LABELER_KEY` - Authentication key for the labeler API
72
+
- `LMSTUDIO_HOST` - LM Studio API host (e.g., `http://localhost:1234`)
73
+
74
+
**For the Skyware Labeler:**
75
+
76
+
- `SKYWARE_DID` - Your labeler's DID
77
+
- `SKYWARE_SIG_KEY` - Your labeler's signing key
78
+
- `EMIT_LABEL_KEY` - Secret key for the emit label API (must match `LABELER_KEY` above)
79
+
80
+
## Running the Services
81
+
82
+
### 1. Start LM Studio
83
+
84
+
1. Open LM Studio
85
+
2. Load a compatible model (recommended: `google/gemma-3-27b` or similar)
86
+
3. Start the local server (usually runs on `http://localhost:1234`)
87
+
88
+
### 2. Start the Labeler Service
89
+
90
+
```bash
91
+
cd labeler
92
+
npm start
93
+
```
94
+
95
+
The labeler will start two servers:
96
+
- Port 14831: Skyware labeler server
97
+
- Port 3000: Label emission API
98
+
99
+
### 3. Start the Go Consumer
100
+
101
+
```bash
102
+
go run .
103
+
```
104
+
105
+
Or build and run:
106
+
107
+
```bash
108
+
go build -o dontshowmethis
109
+
./dontshowmethis
110
+
```
111
+
112
+
## Usage
113
+
114
+
Once running, the system will:
115
+
116
+
1. Connect to Jetstream and monitor the firehose
117
+
2. Watch for replies to accounts specified in `WATCHED_OPS`
118
+
3. Automatically analyze and label qualifying replies
119
+
4. Log all actions to stdout
120
+
121
+
### Finding Account DIDs
122
+
123
+
To monitor specific accounts, you need their DIDs. You can find a DID by:
124
+
125
+
```bash
126
+
curl "https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=username.bsky.social"
127
+
```
128
+
129
+
Add the returned DID to your `WATCHED_OPS` environment variable.
130
+
131
+
## How Content Classification Works
132
+
133
+
See `lmstudio.go:147` for the system prompt.
134
+
135
+
## Development
136
+
137
+
### Project Structure
138
+
139
+
```
140
+
.
141
+
├── main.go # CLI setup and consumer initialization
142
+
├── handle_post.go # Post handling and labeling logic
143
+
├── lmstudio.go # LLM client and content classification
144
+
├── sets/
145
+
│ └── domains.go # Political domain list (currently unused)
146
+
├── labeler/
147
+
│ ├── index.ts # Skyware labeler service
148
+
│ └── package.json # Labeler dependencies
149
+
├── .env.example # Example environment configuration
150
+
└── README.md # This file
151
+
```
152
+
153
+
### Adding New Labels
154
+
155
+
1. Add the label constant in `main.go`:
156
+
```go
157
+
const LabelNewLabel = "new-label"
158
+
```
159
+
160
+
2. Add it to the labeler's allowed labels in `labeler/index.ts`:
161
+
```typescript
162
+
const LABELS: Record<string, boolean> = {
163
+
'bad-faith': true,
164
+
'off-topic': true,
165
+
'funny': true,
166
+
'new-label': true, // Add here
167
+
}
168
+
```
169
+
170
+
3. Update the LLM schema in `lmstudio.go` to include the new classification
171
+
172
+
4. Update the handling logic in `handle_post.go` to emit the new label
173
+
174
+
## License
175
+
176
+
MIT
177
+
178
+
## Acknowledgments
179
+
180
+
- [Jetstream](https://github.com/bluesky-social/jetstream) - Real-time firehose for Bluesky
181
+
- [Skyware](https://skyware.js.org/) - Labeler framework
182
+
- [LM Studio](https://lmstudio.ai/) - Local LLM inference
-61
sets/domains.go
-61
sets/domains.go
···
1
-
package sets
2
-
3
-
var PolDomains = []string{
4
-
"nytimes.com",
5
-
"theguardian.com",
6
-
"open.substack.com",
7
-
"npr.org",
8
-
"cnn.com",
9
-
"washingtonpost.com",
10
-
"wsj.com",
11
-
"newrepublic.com",
12
-
"apnews.com",
13
-
"politico.com",
14
-
"reut.rs",
15
-
"reuters.com",
16
-
"nbcnews.com",
17
-
"kfor.com",
18
-
"huffpost.com",
19
-
"rollingstone.com",
20
-
"rawstory.com",
21
-
"cbsnews.com",
22
-
"bbc.com",
23
-
"cbc.ca",
24
-
"nymag.com",
25
-
"wapo.st",
26
-
"newsweek.com",
27
-
"theatlantic.com",
28
-
"variety.com",
29
-
"nyti.ms",
30
-
"talkingpointsmemo.com",
31
-
"thedailybeast.com",
32
-
"axios.com",
33
-
"publicnotice.co",
34
-
"semafor.com",
35
-
"msnbc.com",
36
-
"liberation.fr",
37
-
"newyorker.com",
38
-
"independent.co.uk",
39
-
"whitehouse.gov",
40
-
"motherjones.com",
41
-
"bbc.co.uk",
42
-
"nbcnews.to",
43
-
"theglobeandmail.com",
44
-
"cnbc.com",
45
-
"defector.com",
46
-
"propublica.com",
47
-
"nypost.com",
48
-
"bloomberg.com",
49
-
"ctvnews.ca",
50
-
"thetimes.com",
51
-
"abc.net.au",
52
-
"bloom.bg",
53
-
"democracydocket.com",
54
-
"cnn.it",
55
-
"latimes.com",
56
-
"publico.es",
57
-
"politico.eu",
58
-
"kyivindependent.com",
59
-
"abcnews.go.com",
60
-
"irishtimes.com",
61
-
}