Commit48ff811eRecorded20 Feb 2026Repositorysigil-xmpp
docs: Add sigil-xmpp package documentation
Changed
docs/xmpp.md | 351 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 351 insertions(+)Diff
docs/xmpp.mdadded
@@ -0,0 +1,351 @@
+1
# XMPP+2
+3
> XMPP client library with STARTTLS, SASL authentication, roster, presence, and MUC support.+4
+5
```scheme+6
(import (sigil xmpp))+7
```+8
+9
## Connecting+10
+11
```scheme+12
(define conn (make-xmpp-connection+13
server: "example.com"+14
jid: "[email protected]"+15
password: "secret"+16
resource: "bot"))+17
+18
;; Install roster, presence, and MUC tracking+19
(xmpp-install-features conn)+20
+21
;; Connect (TCP -> STARTTLS -> SASL -> resource bind)+22
(xmpp-connect conn)+23
+24
;; Announce availability+25
(xmpp-send-presence conn)+26
+27
;; Run the event loop (blocks until disconnected)+28
(xmpp-run conn)+29
```+30
+31
`xmpp-connect` handles the full handshake: TCP connection, STARTTLS upgrade, SASL authentication (SCRAM-SHA-1 or PLAIN), and resource binding. Returns `#t` on success, `#f` on failure.+32
+33
`xmpp-run` processes incoming stanzas. Inside a `with-async` context it cooperates with other tasks via `await-readable`; otherwise it blocks with `socket-select`.+34
+35
## Sending Messages+36
+37
```scheme+38
;; Chat message+39
(xmpp-send conn+40
(xmpp-message to: "[email protected]" body: "Hello!"))+41
+42
;; Message with subject+43
(xmpp-send conn+44
(xmpp-message to: "[email protected]"+45
body: "Check this out"+46
subject: "Interesting"))+47
```+48
+49
## Handling Events (Callbacks)+50
+51
Register handlers for specific event types with `xmpp-on`:+52
+53
```scheme+54
(xmpp-on conn 'message+55
(lambda (stanza)+56
(let ((body (message-body stanza))+57
(from (stanza-from stanza)))+58
(when (and body from)+59
(display (string-append from ": " body "\n"))))))+60
+61
(xmpp-on conn 'presence+62
(lambda (stanza)+63
(display (string-append (stanza-from stanza) " is now "+64
(or (stanza-attr stanza 'type) "available") "\n"))))+65
+66
(xmpp-on conn 'connected+67
(lambda (conn)+68
(display "Connected!\n")))+69
+70
(xmpp-on conn 'disconnected+71
(lambda (conn)+72
(display "Disconnected.\n")))+73
```+74
+75
Event types: `message`, `presence`, `iq`, `stanza`, `connected`, `disconnected`, `error`.+76
+77
`xmpp-on-stanza` registers a handler for all incoming stanzas regardless of type.+78
+79
## Handling Events (Channels)+80
+81
For sequential, composable event processing use `xmpp-channel`:+82
+83
```scheme+84
(import (sigil xmpp)+85
(sigil async)+86
(sigil channels))+87
+88
(with-async+89
(go (xmpp-run conn))+90
+91
(let ((msgs (xmpp-channel conn 'message)))+92
(for-channel msgs+93
(lambda (stanza)+94
(let ((body (message-body stanza)))+95
(when body+96
(xmpp-send conn+97
(xmpp-message to: (stanza-from stanza)+98
body: (string-append "Echo: " body)))))))))+99
```+100
+101
Channels work with `channel-receive`, `for-channel`, and `channel-select` for multiplexing:+102
+103
```scheme+104
(with-async+105
(go (xmpp-run conn))+106
+107
(let ((msgs (xmpp-channel conn 'message))+108
(pres (xmpp-channel conn 'presence)))+109
(let loop ()+110
(channel-select+111
(msgs => (lambda (s) (handle-message s)))+112
(pres => (lambda (s) (handle-presence s))))+113
(loop))))+114
```+115
+116
## IQ Requests+117
+118
Send IQ stanzas with automatic response correlation:+119
+120
```scheme+121
(xmpp-send-iq conn+122
(xmpp-iq type: "get"+123
to: "example.com"+124
children: (list '(query (@ (xmlns "http://jabber.org/protocol/disco#info")))))+125
(lambda (response)+126
(display "Got disco response\n")))+127
```+128
+129
The callback is invoked once when the server replies with a matching IQ result or error.+130
+131
## Roster+132
+133
```scheme+134
;; Fetch the roster+135
(xmpp-request-roster conn+136
(lambda (items)+137
(for-each (lambda (item)+138
(display (string-append (roster-item-jid item) "\n")))+139
items)))+140
+141
;; Access cached roster (after fetch)+142
(xmpp-roster conn) ; => list of roster-item, or #f+143
(xmpp-roster-item conn "[email protected]") ; => roster-item or #f+144
+145
;; Add a contact+146
(xmpp-roster-add conn "[email protected]" name: "Friend")+147
+148
;; Remove a contact+149
(xmpp-roster-remove conn "[email protected]")+150
```+151
+152
### Roster Item Accessors+153
+154
```scheme+155
(roster-item-jid item) ; => "[email protected]"+156
(roster-item-name item) ; => "Alice" or #f+157
(roster-item-subscription item) ; => "both", "from", "to", "none"+158
(roster-item-groups item) ; => ("Friends" "Work")+159
```+160
+161
### Subscriptions+162
+163
```scheme+164
(xmpp-subscribe conn "[email protected]")+165
(xmpp-accept-subscription conn "[email protected]")+166
(xmpp-deny-subscription conn "[email protected]")+167
(xmpp-unsubscribe conn "[email protected]")+168
```+169
+170
## Presence+171
+172
```scheme+173
;; Send available presence+174
(xmpp-send-presence conn)+175
+176
;; Set status+177
(xmpp-set-status conn show: "away" status: "Be right back")+178
+179
;; Go offline and disconnect+180
(xmpp-go-offline conn)+181
```+182
+183
### Querying Presence+184
+185
With `xmpp-install-features`, presence is tracked automatically:+186
+187
```scheme+188
;; Get presence for a full JID+189
(xmpp-presence-of conn "[email protected]/mobile")+190
; => #<presence-info jid: "..." show: "available" status: #f>+191
+192
;; Get all online resources for a bare JID+193
(xmpp-resources-of conn "[email protected]")+194
; => ("[email protected]/mobile" "[email protected]/laptop")+195
```+196
+197
### Presence Info Accessors+198
+199
```scheme+200
(presence-info-jid info) ; => "[email protected]/mobile"+201
(presence-info-show info) ; => "available", "away", "dnd", "xa", "chat"+202
(presence-info-status info) ; => "Be right back" or #f+203
(presence-info-priority info) ; => 0+204
```+205
+206
## Multi-User Chat (MUC)+207
+208
```scheme+209
;; Join a room+210
(xmpp-muc-join conn "[email protected]" "mynick")+211
+212
;; Send a message to the room+213
(xmpp-muc-message conn "[email protected]" "Hello room!")+214
+215
;; Private message to an occupant+216
(xmpp-muc-private-message conn "[email protected]" "alice" "Hi!")+217
+218
;; Set room subject+219
(xmpp-muc-set-subject conn "[email protected]" "New topic")+220
+221
;; Leave the room+222
(xmpp-muc-leave conn "[email protected]")+223
```+224
+225
### Room Administration+226
+227
```scheme+228
;; Kick an occupant (requires moderator role)+229
(xmpp-muc-kick conn "[email protected]" "troublemaker"+230
reason: "Disruptive behavior")+231
+232
;; Invite a user+233
(xmpp-muc-invite conn "[email protected]" "[email protected]"+234
reason: "Come join us!")+235
+236
;; Get current occupants+237
(xmpp-muc-occupants conn "[email protected]")+238
; => list of muc-occupant records+239
```+240
+241
### MUC Occupant Accessors+242
+243
```scheme+244
(muc-occupant-nick occ) ; => "alice"+245
(muc-occupant-jid occ) ; => "[email protected]/res" or #f+246
(muc-occupant-affiliation occ) ; => "owner", "admin", "member", "none"+247
(muc-occupant-role occ) ; => "moderator", "participant", "visitor", "none"+248
```+249
+250
## JIDs+251
+252
```scheme+253
;; Parse a JID string+254
(define j (parse-jid "[email protected]/mobile"))+255
(jid-local j) ; => "user"+256
(jid-domain j) ; => "example.com"+257
(jid-resource j) ; => "mobile"+258
+259
;; Convert back to string+260
(jid->string j) ; => "[email protected]/mobile"+261
+262
;; Bare JID (without resource)+263
(jid-bare "[email protected]/mobile") ; => "[email protected]"+264
(jid-bare j) ; => "[email protected]"+265
```+266
+267
## Stanza Constructors+268
+269
All stanzas are represented as SXML and auto-generate unique IDs:+270
+271
```scheme+272
(xmpp-message to: "[email protected]" body: "Hi" type: "chat")+273
; => (message (@ (to "[email protected]") (type "chat") (id "s1-...")) (body "Hi"))+274
+275
(xmpp-presence show: "away" status: "BRB")+276
; => (presence (@ (id "s2-...")) (show "away") (status "BRB"))+277
+278
(xmpp-iq type: "get" to: "example.com"+279
children: (list '(query (@ (xmlns "jabber:iq:roster")))))+280
; => (iq (@ (to "example.com") (type "get") (id "s3-...")) (query ...))+281
```+282
+283
## Stanza Inspection+284
+285
```scheme+286
(stanza-type stanza) ; => message, presence, iq (symbol)+287
(stanza-to stanza) ; => "[email protected]"+288
(stanza-from stanza) ; => "[email protected]/res"+289
(stanza-id stanza) ; => "s1-abc123"+290
(stanza-attr stanza 'type) ; => "chat"+291
(message-body stanza) ; => "Hello" or #f+292
(stanza-child stanza 'body) ; => (body "Hello") or #f+293
(stanza-children stanza 'item) ; => list of matching child elements+294
```+295
+296
## Common Patterns+297
+298
### Echo Bot+299
+300
```scheme+301
(import (sigil xmpp))+302
+303
(define conn (make-xmpp-connection+304
server: "example.com"+305
jid: "[email protected]"+306
password: "secret"))+307
+308
(xmpp-install-features conn)+309
+310
(xmpp-on conn 'message+311
(lambda (stanza)+312
(let ((body (message-body stanza))+313
(from (stanza-from stanza)))+314
(when (and body from)+315
(xmpp-send conn+316
(xmpp-message to: from body: (string-append "Echo: " body)))))))+317
+318
(xmpp-connect conn)+319
(xmpp-send-presence conn)+320
(xmpp-run conn)+321
```+322
+323
### MUC Bot with Channels+324
+325
```scheme+326
(import (sigil xmpp)+327
(sigil async)+328
(sigil channels))+329
+330
(define conn (make-xmpp-connection+331
server: "example.com"+332
jid: "[email protected]"+333
password: "secret"))+334
+335
(xmpp-install-features conn)+336
(xmpp-connect conn)+337
(xmpp-send-presence conn)+338
(xmpp-muc-join conn "[email protected]" "bot")+339
+340
(with-async+341
(go (xmpp-run conn))+342
+343
(let ((msgs (xmpp-channel conn 'message)))+344
(for-channel msgs+345
(lambda (stanza)+346
(when (equal? (stanza-attr stanza 'type) "groupchat")+347
(let ((body (message-body stanza)))+348
(when (and body (string-starts-with? body "!ping"))+349
(xmpp-muc-message conn+350
"[email protected]" "pong!"))))))))+351
```