Skip to content

Commit

Permalink
import the SimpleChat app, a tool for testing at app-level
Browse files Browse the repository at this point in the history
  • Loading branch information
tschudin committed Aug 3, 2023
1 parent e7fe3f5 commit b6cd5c4
Show file tree
Hide file tree
Showing 16 changed files with 2,987 additions and 0 deletions.
1 change: 1 addition & 0 deletions py/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Python programs for tinySSB

- [simplechat](simplechat) - a TUI client for the tinySSB non-private chat application, based on simplepub
- [simplepub](simplepub) - a server for feed replicas with two interfaces: websocket and BLE

---
4 changes: 4 additions & 0 deletions py/simplechat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# emacs backup files
*!

__pycache__
21 changes: 21 additions & 0 deletions py/simplechat/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Computer Networks Group, University of Basel

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:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

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.
160 changes: 160 additions & 0 deletions py/simplechat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# tinySSB Simple Chat

SimpleChat is a text-user-interface app for reading and writing public
chat messages in tinySSB. It serves mostly as a testing tool.

The main program is ```simplechat.py```. It is built on top of
SimplePub, hence shares several configuration options with
it. Internal logging messages can be seen by toggeling with ESC the
screen between the chat and log view.

In comparison to SimplePub, SimpleChat has a notion of authorship. If
not existing, an ED25519 private key is generated and the corresponding
append-only log is added to the repository of the unterlying
SimplePub. Authorship permits to create and sign new log entries,
which SimplChat requires in order to post messages.

## Description

"tinySSB simple chat" displays all public 'Text-And-Voice' messages found in
a repo's content; new messages can be added. The replication logic running underneath SimpleChat:
- is accessible via web sockets, and
- is accessible via BLE (central-only)
- runs the tinySSB synchronization protocol
- datagram-based, packets have 120 Bytes or less
- growOnlySet protocol for compressing feed IDs
- vectors with WANT and CHNK information
- has adaptive timers, made for reliable connections
- is crash resistant: the ```frontier.bin``` file for a log is updated on startup, should the log have been extended but the frontier failed to be updated

See the README.md of SimplePub for the limitation.

A log entry for the ```TAV``` public chat of tinySSB has the following
format:

```
[ "TAV", # identifies the app
"a text string", # the posted text
#c0dec2010..#, # codec2-encoded voice, or null
1691098012 ] # timestamp
```

This list of four values is BIPF-encoded and stored in the tinySSB
append-only log as a "chain20" log entry type.


## ```simplechat.py``` - a p2p tinySSB chat client

```
usage: simplechat.py [-h] [-data DATAPATH] [-id IDPATH] [-role {in,inout,out}] [-v] [uri_or_port]
positional arguments:
uri_or_port TCP port if responder, URI if intiator (default is ws://127.0.0.1:8080)
options:
-h, --help show this help message and exit
-ble enable Bluetooth Low Energ (default: off)
-data DATAPATH path to persistency directory (default: ./data)
-id IDPATH path to tinySSB private directory (default: ~/.tinySSB)
-role {in,inout,out} direction of data flow (default: inout)
-v print i/o timestamps
```

Example how Alice and Bob start their chat clients (on the same machine):
```
% ./simplechat.py -d ./alice -i ./alice -v 8080 # Alice' clients will respond on websocket port 8080
% ./simplechat.py -d ./bob -i ./bob -v ws://127.0.0.1:8080 # Bob is initiator
```

## Screenshots

The screen of Bob, before sending the reply:
```
-────────── tinyS─────-- <w> connection up─┐
│ │
│ #19 [TVUK3-ZVR4B] │
│ Heyo local butts, what's going on! │
│ _ ⏲ 2023-06-24 19:48:45 │
│ │
│ #20 [6F24K-PBSQ6] │
│ <voice msg> │
│ _ ⏲ 2023-06-26 01:10:18 │
│ │
│ #21 [73ZOO-QOWFW] │
│ Message │
│ _ ⏲ 2023-07-04 07:50:33 │
│ │
│ #22 [QDS2Z-Z3U6W] │
│ Message │
│ _ ⏲ 2023-07-04 08:02:44 │
│ │
│ #23 [7NQQX-YZUDL] │
│ Hi Bob, how are you? Cheers, Alice │
│ _ ⏲ 2023-08-03 23:22:45 │
│ │
┌────────────────────────────────────./bob─┐
│> Hi Alice, all is fine. Best, Bob │
└─────────────────── ESC=log/chat ^C=exit ─┘
```

The screen of Alice, after having received the reply:
```
/────────── tinyS─────────────sending 120B─┐
│ │
│ #20 [6F24K-PBSQ6] │
│ <voice msg> │
│ _ ⏲ 2023-06-26 01:10:18 │
│ │
│ #21 [73ZOO-QOWFW] │
│ Message │
│ _ ⏲ 2023-07-04 07:50:33 │
│ │
│ #22 [QDS2Z-Z3U6W] │
│ Message │
│ _ ⏲ 2023-07-04 08:02:44 │
│ │
│ #23 [7NQQX-YZUDL] │
│ Hi Bob, how are you? Cheers, Alice │
│ _ ⏲ 2023-08-03 23:22:45 │
│ │
│ #24 [LJUGR-FULBS] │
│ Hi Alice, all is fine. Best, Bob │
│ _ ⏲ 2023-08-03 23:26:52 │
│ │
┌──────────────────────────────────./alice─┐
│> │
└─────────────────── ESC=log/chat ^C=exit ─┘
```


Showing the log view (switching is done with ESC)
```
-────^[──── tinyS─────-- <w> connection up─┐
│ │
│ gset dmx 613dfa70c47aba │
│ want dmx 343fc019f3c9ad │
│ chnk dmx 2af30fda04c000 │
│ Starting websocket responder on port 808 │
│ w> 0.000 o=1 61B 0x343fc019f3c9ada40│
│ <w 0.001 i=2 22B 0x2af30fda04c000743│
│ <w 0.001 i=3 105B 0x613dfa70c47aba631│
│ w> 4.002 o=4 61B 0x343fc019f3c9ada40│
│ <w 4.003 i=5 22B 0x2af30fda04c000743│
│ new c=[ 1.6.32 1.7.0 ] │
│ w> 8.004 o=6 61B 0x343fc019f3c9ada40│
│ <w 8.007 i=7 22B 0x2af30fda04c000743│
│ new c=[ 1.6.32 1.7.0 ] │
│ w> 12.006 o=8 61B 0x343fc019f3c9ada4│
│ <w 12.007 i=9 22B 0x2af30fda04c00074│
│ <w 15.002 i=10 105B 0x613dfa70c47aba63│
│ w> 16.008 o=11 61B 0x343fc019f3c9ada4│
│ <w 16.010 i=12 22B 0x2af30fda04c00074│
│ new c=[ 1.6.32 1.7.0 ] │
│ =C [ 1.6.32 1.7.0 ] [] │
┌──────────────────────────────────./alice─┐
│> │
└─────────────────── ESC=log/chat ^C=exit ─┘
```

---
94 changes: 94 additions & 0 deletions py/simplechat/frontier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env python3

# frontier.py
# display local frontier state

# Jul 2023 <christian.tschudin@unibas.ch>

# ---------------------------------------------------------------------------

def bytes2hex(d):
if type(d) == list:
return [bytes2hex(x) for x in d]
if type(d) == dict:
return {bytes2hex(k):bytes2hex(v) for k,v in d.items()}
if type(d) == bytes:
return f'#{d.hex()}#'
return d

def bytes2content(buf):
if buf == None:
return '||'
try:
buf = bipf.loads(buf)
return f"bipf({bytes2hex(buf)})"
except:
return buf.decode('utf-8', 'replace')

if __name__ == '__main__':

import argparse
import os

from simplepub import bipf, replica

ap = argparse.ArgumentParser()
ap.add_argument('-d', type=str, default='./data', metavar='DATAPATH',
help='path to persistency directory')
ap.add_argument('-raw', action='store_true', default=False,
help='dump raw log entries and side chain packets')
ap.add_argument('-stat', action="store_true", default=False,
help='only show stats (no content), default: False')
args = ap.parse_args()

keys = [ bytes.fromhex(fn) for fn in os.listdir(args.d)
if len(fn) == 64 and os.path.isdir(args.d + '/' + fn)]
keys.sort()

cnt_entries = 0
cnt_chunks = 0
cnt_missing = 0
missing = []

for i in range(len(keys)):
fid = keys[i];
r = replica.Replica(args.d,fid,None)
if not args.stat:
print(f"* key {i} {fid.hex()}")
ms = r.state['max_seq']
cnt_entries += ms
if not args.stat:
print(f" max_seq = {ms}, prev = {r.state['prev'].hex()}")
psc = r.state['pend_sc']
for k in range(ms):
e = r.get_entry_pkt(k+1)
if e[7] == 1 and not k+1 in psc: # chain20 with full sidechain
clen, sz = bipf.varint_decode(e[8:])
clen -= 48 - 20 - sz
if clen > 0:
cnt_chunks += (clen + 99) // 100
if len(psc) != 0:
for s,v in psc.items():
missing.append(f"{i}.{s}.{v[0]}ff")
cnt_chunks += v[0]
cnt_missing += v[1]
psc = {s:f"{v[0]}/{v[0]+v[1]}" for s,v in psc.items()}
if not args.stat:
print(f" pend_sc = {psc}")
if ms > 0 and not args.stat:
for k in range(ms):
a,l = r.get_content_len(k+1)
print(f" #{k+1} "[:10] + f" {a}/{l} "[-13:], end='')
if args.raw:
print(r.read(k+1).hex())
else:
print(bytes2content(r.read(k+1)))

if not args.stat:
print()
print("Stats:")
print(f"- {len(keys)} feeds")
print(f"- {cnt_entries} available entries")
print(f"- {cnt_chunks} available chunks")
print(f"- {cnt_missing} missing chunks: {', '.join(missing)}")
# eof
26 changes: 26 additions & 0 deletions py/simplechat/pure25519/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
2022-03-21 <christian.tschudin@unibas.ch> moving/merging code among files

"python-pure25519" Copyright (c) 2015 Brian Warner and other contributors
https://github.com/warner/python-pure25519

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:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

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.

Loading

0 comments on commit b6cd5c4

Please sign in to comment.