@solarpunkltd/swarm-chat-js 4.2.1 → 4.2.3
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.
- package/README.md +232 -105
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,131 +1,258 @@
|
|
|
1
|
-
|
|
1
|
+
# Swarm Chat JS Library 🐝💬
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The core client-side library for building decentralized chat applications over [Swarm](https://www.ethswarm.org/). This library provides the essential logic for sending, receiving, and managing chat messages within a Swarm ecosystem.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Important Note:** `swarm-chat-js` is designed to work in conjunction with a companion aggregator server. It is **not functional as a standalone library** for a complete chat system. The aggregator is responsible for collecting messages broadcast by users and consolidating them into a shared chat feed.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The current reference implementation works with the [Solar-Punk-Ltd/swarm-chat-aggregator-js](https://github.com/Solar-Punk-Ltd/swarm-chat-aggregator-js).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ⚙️ How It Works
|
|
12
|
+
|
|
13
|
+
`swarm-chat-js` enables decentralized communication by leveraging Swarm's features like Feeds and GSOC.
|
|
14
|
+
|
|
15
|
+
**Conceptual Overview:**
|
|
16
|
+
|
|
17
|
+
Users broadcast their messages via updates to their personal Swarm feeds, with notifications sent to a pre-defined GSOC address. A dedicated **aggregator server** listens to this GSOC message. When new message notifications arrive, the aggregator fetches the message content, processes it, and then writes it to a common, persistent, protected Swarm feed (the "chat feed"). Client applications polling this aggregated chat feed to display messages to users (built into `swarm-chat-js`).
|
|
18
|
+
|
|
19
|
+
**Message Flow:**
|
|
20
|
+
|
|
21
|
+
1. **User Sends Message:** A user types and sends a message using an application built with `swarm-chat-js`.
|
|
22
|
+
- The message is written to the user's own Swarm feed.
|
|
23
|
+
- An update is broadcast to a designated GSOC address.
|
|
24
|
+
2. **Aggregator Receives & Processes:** The aggregator server, subscribed to the GSOC address, receives the update.
|
|
25
|
+
- It may perform validation or other processing steps.
|
|
26
|
+
3. **Aggregator Writes to Chat Feed:** The aggregator writes the processed message to the main, shared "chat feed".
|
|
27
|
+
4. **Client App Reads:** `swarm-chat-js` in other users' applications polls this main chat feed for new messages and displays them.
|
|
28
|
+
|
|
29
|
+
---
|
|
19
30
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- gsocResourceId: Required for GSOC functionality. You must mine this ID using the mineResourceId function.
|
|
23
|
-
- Learn more about GSOC:
|
|
24
|
-
https://blog.ethswarm.org/foundation/2024/bee-2-3-pre-release/
|
|
25
|
-
https://github.com/ethersphere/SWIPs/blob/99e6cf90a4768b24d27e5339b205c18825b53322/SWIPs/swip-draft_graffiti-soc.md
|
|
26
|
-
The project uses a forked version of @anythread/gsoc for GSOC nodes.
|
|
27
|
-
|
|
28
|
-
- topic: A simple string used to categorize the chat.
|
|
29
|
-
- nickname: A user-friendly name for identification.
|
|
30
|
-
- bees: Infrastructure settings for the library.
|
|
31
|
-
Infrastructure Example:
|
|
32
|
-
```
|
|
33
|
-
bees: {
|
|
34
|
-
// example infrastructure settings
|
|
35
|
-
multiBees: {
|
|
36
|
-
gsoc: {
|
|
37
|
-
multiBees: [
|
|
38
|
-
{
|
|
39
|
-
url: "",
|
|
40
|
-
main: true,
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
url: "",
|
|
44
|
-
stamp: "" as BatchId,
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
writer: {
|
|
49
|
-
singleBee: {
|
|
50
|
-
url: "",
|
|
51
|
-
stamp: "" as BatchId,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
reader: {
|
|
55
|
-
singleBee: {
|
|
56
|
-
url: "",
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
```
|
|
62
|
-
- multiBees: Use multiple nodes for better performance.
|
|
63
|
-
Main GSOC Node: Handles incoming messages.
|
|
64
|
-
Writer Nodes: Write messages to the shared GSOC address.
|
|
65
|
-
Reader Nodes: Read messages from the feeds.
|
|
66
|
-
|
|
67
|
-
- ownAddress: The public address of your wallet for authentication.
|
|
68
|
-
- privateKey: The private key of your wallet for feed writing. Do not use a wallet storing assets.
|
|
69
|
-
|
|
70
|
-
Starting and Stopping
|
|
71
|
-
|
|
72
|
-
- Start Chat: Use the start() method to subscribe to events and initialize intervals.
|
|
73
|
-
- Stop Chat: Use the stop() method to clean up resources.
|
|
74
|
-
|
|
75
|
-
Sending a Message
|
|
76
|
-
|
|
77
|
-
- Use the sendMessage(message: string) method to send a message.
|
|
78
|
-
|
|
79
|
-
Listening to Events
|
|
80
|
-
The library provides several events that can be subscribed to:
|
|
31
|
+
## 📦 Installation
|
|
81
32
|
|
|
33
|
+
You can install the library using npm or pnpm:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm/pnpm install @solarpunkltd/swarm-chat-js
|
|
82
37
|
```
|
|
83
|
-
const { on } = newChat.getEmitter();
|
|
84
38
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 🛠️ Core Concepts & API
|
|
88
42
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
43
|
+
### Imports
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { EVENTS, MessageData, SwarmChat, ChatSettings } from '@solarpunkltd/swarm-chat-js';
|
|
47
|
+
```
|
|
92
48
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
49
|
+
### `ChatSettings` Interface
|
|
50
|
+
|
|
51
|
+
This configuration object is crucial for initializing the `SwarmChat` instance.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
export interface ChatSettings {
|
|
55
|
+
user: {
|
|
56
|
+
/** Private key of the chat user, used for signing updates to their own Swarm feed. */
|
|
57
|
+
privateKey: string;
|
|
58
|
+
/** Display name or nickname of the current user. */
|
|
59
|
+
nickname: string;
|
|
60
|
+
};
|
|
61
|
+
infra: {
|
|
62
|
+
/** URL of the Bee node used by the client to write to their own feed and to poll the aggregated chat feed. */
|
|
63
|
+
beeUrl: string;
|
|
64
|
+
/**
|
|
65
|
+
* If true, a postage stamp (`stamp` property) must be provided for uploading messages.
|
|
66
|
+
* The stamp does not necessarily need to be tied to the `beeUrl` node; it can be an independent stamp. (Enveloped stamp)
|
|
67
|
+
*/
|
|
68
|
+
enveloped: boolean;
|
|
69
|
+
/** Optional: Postage stamp ID. Required if `enveloped` is true, unless `beeUrl` points to a gateway with auto-stamping capabilities. */
|
|
70
|
+
stamp?: string;
|
|
71
|
+
/** The mined GSOC topic string where users broadcast updates about their new messages. */
|
|
72
|
+
gsocTopic: string;
|
|
73
|
+
/** The mined GSOC resource ID (address) associated with the `gsocTopic`. */
|
|
74
|
+
gsocResourceId: string;
|
|
75
|
+
/** The topic of the aggregated chat feed, written by the aggregator server. */
|
|
76
|
+
chatTopic: string;
|
|
77
|
+
/** The public address (Swarm feed address) of the aggregated chat feed, written by the aggregator. */
|
|
78
|
+
chatAddress: string;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
```
|
|
96
82
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
### Events (`EVENTS`)
|
|
84
|
+
|
|
85
|
+
The library emits several events that your application can subscribe to for reacting to different stages of the chat lifecycle. Use `SwarmChat.getEmitter().on(EVENT_NAME, callback)` to subscribe.
|
|
86
|
+
|
|
87
|
+
- `EVENTS.LOADING_INIT`: ('loadingInit')
|
|
88
|
+
Fired when the chat library begins its initialization process.
|
|
89
|
+
- `EVENTS.LOADING_PREVIOUS_MESSAGES`: ('loadingPreviousMessages')
|
|
90
|
+
Fired when the library is actively loading previous messages from the chat feed.
|
|
91
|
+
- `EVENTS.MESSAGE_RECEIVED`: ('messageReceived')
|
|
92
|
+
A new message has been successfully received from the aggregated chat feed and processed by the client.
|
|
93
|
+
- `EVENTS.MESSAGE_REQUEST_INITIATED`: ('messageRequestInitiated')
|
|
94
|
+
The current user has initiated the process of sending a new message.
|
|
95
|
+
- `EVENTS.MESSAGE_REQUEST_UPLOADED`: ('messageRequestUploaded')
|
|
96
|
+
The current user's message has been successfully uploaded to their own feed. (Note: The broadcast to the GSOC for aggregator pickup can still be pending or fail after this event).
|
|
97
|
+
- `EVENTS.MESSAGE_REQUEST_ERROR`: ('messageRequestError')
|
|
98
|
+
An error occurred during the message sending process (either uploading to the user's feed or broadcasting via GSOC).
|
|
99
|
+
- `EVENTS.CRITICAL_ERROR`: ('criticalError')
|
|
100
|
+
The library has encountered a critical, potentially unrecoverable error.
|
|
101
|
+
|
|
102
|
+
### Main `SwarmChat` Methods
|
|
103
|
+
|
|
104
|
+
The `SwarmChat` class instance provides the following core methods:
|
|
105
|
+
|
|
106
|
+
- `start()`: Initializes and starts the chat service, including setting up listeners and beginning to poll for messages.
|
|
107
|
+
- `stop()`: Stops the chat service, clears intervals, and cleans up resources.
|
|
108
|
+
- `getEmitter()`: Returns an event emitter instance, allowing your application to subscribe to the `EVENTS` listed above.
|
|
109
|
+
- `sendMessage(message: string)`: Initiates the process of sending a new chat message from the current user.
|
|
110
|
+
- `fetchPreviousMessages()`: Manually triggers the fetching of older messages from the aggregated chat feed.
|
|
111
|
+
- `retrySendMessage(message: MessageData)`: Attempts to resend a message that previously encountered an error during the initial request phase (e.g., failed to write to the user's own feed).
|
|
112
|
+
- `retryBroadcastUserMessage(message: MessageData)`: Attempts to re-broadcast a message update via GSOC if the message was successfully uploaded to the user's feed but the GSOC broadcast might have failed or needs retrying.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 🚀 Usage Example (React)
|
|
117
|
+
|
|
118
|
+
Here's an example of how `swarm-chat-js` can be integrated into a React application using a custom hook (`useSwarmChat`). This hook encapsulates chat logic, state management, and event handling.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { useEffect, useRef, useState } from 'react';
|
|
122
|
+
import { EVENTS, MessageData, SwarmChat, ChatSettings } from '@solarpunkltd/swarm-chat-js';
|
|
123
|
+
|
|
124
|
+
export interface VisibleMessage extends MessageData {
|
|
125
|
+
requested?: boolean;
|
|
126
|
+
uploaded?: boolean;
|
|
127
|
+
received?: boolean;
|
|
128
|
+
error?: boolean;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const useSwarmChat = ({ user, infra }: ChatSettings) => {
|
|
132
|
+
const chat = useRef<SwarmChat | null>(null);
|
|
133
|
+
const messageCache = useRef<VisibleMessage[]>([]);
|
|
134
|
+
const [allMessages, setAllMessages] = useState<VisibleMessage[]>([]);
|
|
135
|
+
const [chatLoading, setChatLoading] = useState<boolean>(true);
|
|
136
|
+
const [messagesLoading, setMessagesLoading] = useState<boolean>(false);
|
|
137
|
+
const [error, setError] = useState<any | null>(null);
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!chat.current) {
|
|
141
|
+
const newChat = new SwarmChat({
|
|
142
|
+
user,
|
|
143
|
+
infra,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
chat.current = newChat;
|
|
147
|
+
|
|
148
|
+
const { on } = newChat.getEmitter();
|
|
149
|
+
|
|
150
|
+
const updateMessage = (id: string, updates: Partial<VisibleMessage>) => {
|
|
151
|
+
messageCache.current = messageCache.current.map((msg) => (msg.id === id ? { ...msg, ...updates } : msg));
|
|
152
|
+
setAllMessages(chat.current?.orderMessages([...messageCache.current]) || []);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const handleMessageEvent = (event: string, updates: Partial<VisibleMessage>) => {
|
|
156
|
+
on(event, (d: MessageData | string) => {
|
|
157
|
+
const data = typeof d === 'string' ? JSON.parse(d) : d;
|
|
158
|
+
|
|
159
|
+
const existingMessage = messageCache.current.find((msg) => msg.id === data.id);
|
|
160
|
+
if (existingMessage) {
|
|
161
|
+
updateMessage(data.id, updates);
|
|
162
|
+
} else {
|
|
163
|
+
messageCache.current.push({ ...data, ...updates });
|
|
164
|
+
setAllMessages(chat.current?.orderMessages([...messageCache.current]) || []);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
handleMessageEvent(EVENTS.MESSAGE_REQUEST_INITIATED, {
|
|
170
|
+
error: false,
|
|
171
|
+
requested: true,
|
|
172
|
+
});
|
|
173
|
+
handleMessageEvent(EVENTS.MESSAGE_REQUEST_UPLOADED, {
|
|
174
|
+
error: false,
|
|
175
|
+
uploaded: true,
|
|
176
|
+
});
|
|
177
|
+
handleMessageEvent(EVENTS.MESSAGE_RECEIVED, {
|
|
178
|
+
error: false,
|
|
179
|
+
received: true,
|
|
180
|
+
});
|
|
181
|
+
handleMessageEvent(EVENTS.MESSAGE_REQUEST_ERROR, { error: true });
|
|
182
|
+
|
|
183
|
+
on(EVENTS.LOADING_INIT, setChatLoading);
|
|
184
|
+
on(EVENTS.LOADING_PREVIOUS_MESSAGES, setMessagesLoading);
|
|
185
|
+
on(EVENTS.CRITICAL_ERROR, setError);
|
|
186
|
+
|
|
187
|
+
newChat.start();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return () => {
|
|
191
|
+
if (chat.current) {
|
|
192
|
+
chat.current.stop();
|
|
193
|
+
chat.current = null;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}, [user.privateKey]);
|
|
197
|
+
|
|
198
|
+
const sendMessage = (message: string) => chat.current?.sendMessage(message);
|
|
199
|
+
|
|
200
|
+
const fetchPreviousMessages = () => chat.current?.fetchPreviousMessages();
|
|
201
|
+
|
|
202
|
+
const retrySendMessage = (message: VisibleMessage) => {
|
|
203
|
+
if (message.requested && message.error) {
|
|
204
|
+
chat.current?.retrySendMessage(message);
|
|
205
|
+
}
|
|
206
|
+
if (message.uploaded && message.error) {
|
|
207
|
+
chat.current?.retryBroadcastUserMessage(message);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
chatLoading,
|
|
213
|
+
messagesLoading,
|
|
214
|
+
allMessages,
|
|
215
|
+
sendMessage,
|
|
216
|
+
fetchPreviousMessages,
|
|
217
|
+
retrySendMessage,
|
|
218
|
+
error,
|
|
219
|
+
};
|
|
220
|
+
};
|
|
101
221
|
```
|
|
102
222
|
|
|
103
|
-
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## ⛏️ Helper Scripts
|
|
104
226
|
|
|
105
|
-
|
|
227
|
+
### Mine GSOC Address
|
|
106
228
|
|
|
107
|
-
|
|
229
|
+
The library provides a helper script to mine a GSOC address and topic. This is typically used when setting up your aggregator server.
|
|
108
230
|
|
|
109
|
-
|
|
231
|
+
**Usage:**
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
npm run mine -- <bee-address> <topic-name>
|
|
235
|
+
```
|
|
110
236
|
|
|
111
|
-
|
|
237
|
+
---
|
|
112
238
|
|
|
113
|
-
|
|
239
|
+
## ⚠️ Limitations
|
|
114
240
|
|
|
115
|
-
|
|
241
|
+
- **Polling Mechanism:** The current version of `swarm-chat-js` relies on polling the aggregator's chat feed to fetch new messages. This approach can be resource-heavy.
|
|
116
242
|
|
|
117
|
-
|
|
243
|
+
---
|
|
118
244
|
|
|
119
|
-
|
|
245
|
+
## 💡 Future Development
|
|
120
246
|
|
|
121
|
-
|
|
247
|
+
- **Push-Based Event System:** We are actively designing a new architecture to transition from the polling mechanism to a more efficient, push-based event system for message delivery. This will significantly reduce node load and improve real-time message propagation. Stay tuned! ☀️
|
|
122
248
|
|
|
123
|
-
|
|
249
|
+
---
|
|
124
250
|
|
|
125
|
-
|
|
251
|
+
## 📚 Further Reading & Resources
|
|
126
252
|
|
|
127
|
-
- https://docs.ethswarm.org/docs/develop/tools-and-features/feeds#what-are-feeds
|
|
128
|
-
- https://docs.ethswarm.org/docs/develop/tools-and-features/gsoc/#introduction
|
|
253
|
+
- [What are Feeds? (Official Swarm Documentation)](https://docs.ethswarm.org/docs/develop/tools-and-features/feeds#what-are-feeds)
|
|
254
|
+
- [GSOC Introduction (Official Swarm Documentation)](https://docs.ethswarm.org/docs/develop/tools-and-features/gsoc/#introduction)
|
|
255
|
+
- [Example Aggregator: Solar-Punk-Ltd/swarm-chat-aggregator-js](https://github.com/Solar-Punk-Ltd/swarm-chat-aggregator-js)
|
|
256
|
+
- [Example React client](https://github.com/Solar-Punk-Ltd/swarm-chat-react-example)
|
|
129
257
|
|
|
130
|
-
|
|
131
|
-
https://github.com/Solar-Punk-Ltd/swarm-chat-react-example
|
|
258
|
+
---
|