From 754ae969e10d6ee1002dd31bf130573ef30b1e4d Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 3 Jan 2024 12:45:18 +0100 Subject: [PATCH] Added code examples. Closes #8. --- README.md | 4 ++++ docs/example_receiver.py | 37 +++++++++++++++++++++++++++++++++++++ docs/example_sender.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 docs/example_receiver.py create mode 100644 docs/example_sender.py diff --git a/README.md b/README.md index bd62246..11451a8 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,10 @@ Assuming the default Reticulum configuration, the binary wire-format is as follo The complete message overhead for LXMF is only 111 bytes, which in return gives you timestamped, digitally signed, infinitely extensible, end-to-end encrypted, zero-conf routed, minimal-infrastructure messaging that's easy to use and build applications with. +## Code Examples + +Before writing your own programs using LXMF, you need to have a basic understanding of how the [Reticulum](https://reticulum.network) protocol and API works. Please see the [Reticulum Manual](https://reticulum.network/manual/). For a few simple examples of how to send and receive messages with LXMF, please see the [receiver example](./docs/example_receiver.py) and the [sender example](./docs/example_sender.py) included in this repository. + ## Example Paper Message You can try out the paper messaging functionality by using the following QR code. It is a paper message sent to the LXMF address `6b3362bd2c1dbf87b66a85f79a8d8c75`. To be able to decrypt and read the message, you will need to import the following Reticulum Identity to an LXMF messaging app: diff --git a/docs/example_receiver.py b/docs/example_receiver.py new file mode 100644 index 0000000..a637cd4 --- /dev/null +++ b/docs/example_receiver.py @@ -0,0 +1,37 @@ +import RNS +import LXMF +import time + +def delivery_callback(message): + time_string = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(message.timestamp)) + signature_string = "Signature is invalid, reason undetermined" + if message.signature_validated: + signature_string = "Validated" + else: + if message.unverified_reason == LXMF.LXMessage.SIGNATURE_INVALID: + signature_string = "Invalid signature" + if message.unverified_reason == LXMF.LXMessage.SOURCE_UNKNOWN: + signature_string = "Cannot verify, source is unknown" + + RNS.log("\t+--- LXMF Delivery ---------------------------------------------") + RNS.log("\t| Source hash : "+RNS.prettyhexrep(message.source_hash)) + RNS.log("\t| Source instance : "+str(message.get_source())) + RNS.log("\t| Destination hash : "+RNS.prettyhexrep(message.destination_hash)) + RNS.log("\t| Destination instance : "+str(message.get_destination())) + RNS.log("\t| Transport Encryption : "+str(message.transport_encryption)) + RNS.log("\t| Timestamp : "+time_string) + RNS.log("\t| Title : "+message.title_as_string()) + RNS.log("\t| Content : "+message.content_as_string()) + RNS.log("\t| Fields : "+str(message.fields)) + RNS.log("\t| Message signature : "+signature_string) + RNS.log("\t+---------------------------------------------------------------") + +r = RNS.Reticulum() + +router = LXMF.LXMRouter(storagepath="./tmp1") +identity = RNS.Identity() +my_lxmf_destination = router.register_delivery_identity(identity) +router.register_delivery_callback(delivery_callback) + +RNS.log("Ready to receive on: "+RNS.prettyhexrep(my_lxmf_destination.hash)) +input() \ No newline at end of file diff --git a/docs/example_sender.py b/docs/example_sender.py new file mode 100644 index 0000000..ab15a16 --- /dev/null +++ b/docs/example_sender.py @@ -0,0 +1,39 @@ +import LXMF +import RNS +import time +import random + +random_names = ["Tom", "Delilah", "Nancey", "Williams", "Neomi", "Curtis", "Alexa", "Theodora", "Ted", "Dinorah", "Nicol", "Drusilla", "Annalisa", "Verlene", "Latesha", "Tina", "Mia", "Brock", "Timothy", "Philip", "Willian", "Reyna", "Simona", "Mimi", "Stanford", "Ferne", "Catalina", "Lucie", "Jaye", "Natasha", "Willetta", "Isabel", "Esperanza", "Ciara", "Eusebio", "William", "Elma", "Angelica", "Coreen", "Melani", "Jonathan", "Maryland", "Caroline", "Gregg", "Ora", "Jacqui", "Letty", "Roselle", "Oralee", "Angla"] +random_titles = ["Long time", "Hi again", "Re: Hi there", "Test message", "", "", "Something different"] +random_msgs = ["If wishes were horses then beggars might fly. Stuff like that. It's enough to drive you crazy.", "'My ident cards were stolen,' Jason said. 'That fivehundred-dollar bill is yours if you can get me to someone who can replace them. If you're going to do it, do it right now; I'm not going to wait.' Wait to be picked up by a pol or a nat, he thought. Caught here in this rundown dingy hotel.", "A six, no matter what the external circumstances, will always prevail. Because that's the way they genetically defined us.", "'Should be there in an hour,' he called back over his shoulder to Chuck. Then he added, in an afterthought, 'Wonder if the computer’s finished its run. It was due about now.'. Chuck didn’t reply, so George swung round in his saddle. He could just see Chuck’s face, a white oval turned toward the sky."] + +def delivery_callback(message): + pass + +r = RNS.Reticulum() +router = LXMF.LXMRouter(storagepath="./tmp2") +router.register_delivery_callback(delivery_callback) +ident = RNS.Identity() +source = router.register_delivery_identity(ident, display_name=random_names[random.randint(0,len(random_names)-1)]) +router.announce(source.hash) +RNS.log("Source announced") + +print("Recipient: ", end=" ") +recipient_hexhash = input() +recipient_hash = bytes.fromhex(recipient_hexhash) + +if not RNS.Transport.has_path(recipient_hash): + RNS.log("Destination is not yet known. Requesting path and waiting for announce to arrive...") + RNS.Transport.request_path(recipient_hash) + while not RNS.Transport.has_path(recipient_hash): + time.sleep(0.1) + +# Recall the server identity +recipient_identity = RNS.Identity.recall(recipient_hash) + +dest = RNS.Destination(recipient_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery") + +while True: + lxm = LXMF.LXMessage(dest, source, random_msgs[random.randint(0,len(random_msgs)-1)], random_titles[random.randint(0,len(random_titles)-1)], desired_method=LXMF.LXMessage.DIRECT) + router.handle_outbound(lxm) + input() \ No newline at end of file