Updated formatting of examples for better inclusion in documentation.
This commit is contained in:
		
							parent
							
								
									eb0c40295f
								
							
						
					
					
						commit
						11fe8b64f8
					
				| @ -15,7 +15,7 @@ import RNS | ||||
| APP_NAME = "example_utilities" | ||||
| 
 | ||||
| # We initialise two lists of strings to use as app_data | ||||
| fruits = ["Peach", "Quince", "Date palm", "Tangerine", "Pomelo", "Carambola", "Grape", "Passion fruit", "Prune", "Cranberry", "Strawberry", "Papaya", "Pomegranate", "Avocado", "Mango"] | ||||
| fruits = ["Peach", "Quince", "Date palm", "Tangerine", "Pomelo", "Carambola", "Grape"] | ||||
| noble_gases = ["Helium", "Neon", "Argon", "Krypton", "Xenon", "Radon", "Oganesson"] | ||||
| 
 | ||||
| # This initialisation is executed when the program is started | ||||
| @ -34,8 +34,23 @@ def program_setup(configpath): | ||||
|     # existence, which will let the network know they are reachable | ||||
|     # and autoomatically create paths to them, from anywhere else | ||||
|     # in the network. | ||||
|     destination_1 = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "announcesample", "fruits") | ||||
|     destination_2 = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "announcesample", "noble_gases") | ||||
|     destination_1 = RNS.Destination( | ||||
|         identity, | ||||
|         RNS.Destination.IN, | ||||
|         RNS.Destination.SINGLE, | ||||
|         APP_NAME, | ||||
|         "announcesample", | ||||
|         "fruits" | ||||
|     ) | ||||
| 
 | ||||
|     destination_2 = RNS.Destination( | ||||
|         identity, | ||||
|         RNS.Destination.IN, | ||||
|         RNS.Destination.SINGLE, | ||||
|         APP_NAME, | ||||
|         "announcesample", | ||||
|         "noble_gases" | ||||
|     ) | ||||
| 
 | ||||
|     # We configure the destinations to automatically prove all | ||||
|     # packets adressed to it. By doing this, RNS will automatically | ||||
| @ -49,7 +64,9 @@ def program_setup(configpath): | ||||
|     # We create an announce handler and configure it to only ask for | ||||
|     # announces from "example_utilities.announcesample.fruits". | ||||
|     # Try changing the filter and see what happens. | ||||
|     announce_handler = ExampleAnnounceHandler(aspect_filter="example_utilities.announcesample.fruits") | ||||
|     announce_handler = ExampleAnnounceHandler( | ||||
|         aspect_filter="example_utilities.announcesample.fruits" | ||||
|     ) | ||||
| 
 | ||||
|     # We register the announce handler with Reticulum | ||||
|     RNS.Transport.register_announce_handler(announce_handler) | ||||
| @ -75,14 +92,22 @@ def announceLoop(destination_1, destination_2): | ||||
| 
 | ||||
|         # Send the announce including the app data | ||||
|         destination_1.announce(app_data=fruit.encode("utf-8")) | ||||
|         RNS.log("Sent announce from "+RNS.prettyhexrep(destination_1.hash)+" ("+destination_1.name+")") | ||||
|         RNS.log( | ||||
|             "Sent announce from "+ | ||||
|             RNS.prettyhexrep(destination_1.hash)+ | ||||
|             " ("+destination_1.name+")" | ||||
|         ) | ||||
| 
 | ||||
|         # Randomly select a noble gas | ||||
|         noble_gas = noble_gases[random.randint(0,len(noble_gases)-1)] | ||||
| 
 | ||||
|         # Send the announce including the app data | ||||
|         destination_2.announce(app_data=noble_gas.encode("utf-8")) | ||||
|         RNS.log("Sent announce from "+RNS.prettyhexrep(destination_2.hash)+" ("+destination_2.name+")") | ||||
|         RNS.log( | ||||
|             "Sent announce from "+ | ||||
|             RNS.prettyhexrep(destination_2.hash)+ | ||||
|             " ("+destination_2.name+")" | ||||
|         ) | ||||
| 
 | ||||
| # We will need to define an announce handler class that | ||||
| # Reticulum can message when an announce arrives. | ||||
| @ -100,8 +125,15 @@ class ExampleAnnounceHandler: | ||||
|     # configured aspect filter. Filters must be specific, | ||||
|     # and cannot use wildcards. | ||||
|     def received_announce(self, destination_hash, announced_identity, app_data): | ||||
|         RNS.log("Received an announce from "+RNS.prettyhexrep(destination_hash)) | ||||
|         RNS.log("The announce contained the following app data: "+app_data.decode("utf-8")) | ||||
|         RNS.log( | ||||
|             "Received an announce from "+ | ||||
|             RNS.prettyhexrep(destination_hash) | ||||
|         ) | ||||
| 
 | ||||
|         RNS.log( | ||||
|             "The announce contained the following app data: "+ | ||||
|             app_data.decode("utf-8") | ||||
|         ) | ||||
| 
 | ||||
| ########################################################## | ||||
| #### Program Startup ##################################### | ||||
| @ -112,8 +144,18 @@ class ExampleAnnounceHandler: | ||||
| # the desired program mode. | ||||
| if __name__ == "__main__": | ||||
|     try: | ||||
|         parser = argparse.ArgumentParser(description="Reticulum example that demonstrates announces and announce handlers") | ||||
|         parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str) | ||||
|         parser = argparse.ArgumentParser( | ||||
|             description="Reticulum example that demonstrates announces and announce handlers" | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "--config", | ||||
|             action="store", | ||||
|             default=None, | ||||
|             help="path to alternative Reticulum config directory", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         args = parser.parse_args() | ||||
| 
 | ||||
|         if args.config: | ||||
|  | ||||
| @ -28,7 +28,14 @@ def program_setup(configpath, channel=None): | ||||
| 
 | ||||
|     # We create a PLAIN destination. This is an uncencrypted endpoint | ||||
|     # that anyone can listen to and send information to. | ||||
|     broadcast_destination = RNS.Destination(None, RNS.Destination.IN, RNS.Destination.PLAIN, APP_NAME, "broadcast", channel) | ||||
|     broadcast_destination = RNS.Destination( | ||||
|         None, | ||||
|         RNS.Destination.IN, | ||||
|         RNS.Destination.PLAIN, | ||||
|         APP_NAME, | ||||
|         "broadcast", | ||||
|         channel | ||||
|     ) | ||||
| 
 | ||||
|     # We specify a callback that will get called every time | ||||
|     # the destination receives data. | ||||
| @ -46,7 +53,11 @@ def packet_callback(data, packet): | ||||
| 
 | ||||
| def broadcastLoop(destination): | ||||
|     # Let the user know that everything is ready | ||||
|     RNS.log("Broadcast example "+RNS.prettyhexrep(destination.hash)+" running, enter text and hit enter to broadcast (Ctrl-C to quit)") | ||||
|     RNS.log( | ||||
|         "Broadcast example "+ | ||||
|         RNS.prettyhexrep(destination.hash)+ | ||||
|         " running, enter text and hit enter to broadcast (Ctrl-C to quit)" | ||||
|     ) | ||||
| 
 | ||||
|     # We enter a loop that runs until the users exits. | ||||
|     # If the user hits enter, we will send the information | ||||
| @ -71,9 +82,26 @@ def broadcastLoop(destination): | ||||
| # the program. | ||||
| if __name__ == "__main__": | ||||
|     try: | ||||
|         parser = argparse.ArgumentParser(description="Reticulum example that demonstrates sending and receiving unencrypted broadcasts") | ||||
|         parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str) | ||||
|         parser.add_argument("--channel", action="store", default=None, help="broadcast channel name", type=str) | ||||
|         parser = argparse.ArgumentParser( | ||||
|             description="Reticulum example demonstrating sending and receiving broadcasts" | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "--config", | ||||
|             action="store", | ||||
|             default=None, | ||||
|             help="path to alternative Reticulum config directory", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "--channel", | ||||
|             action="store", | ||||
|             default=None, | ||||
|             help="broadcast channel name", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         args = parser.parse_args() | ||||
| 
 | ||||
|         if args.config: | ||||
|  | ||||
| @ -34,7 +34,14 @@ def server(configpath): | ||||
|     # messages. This way the client can send a request and be | ||||
|     # certain that no-one else than this destination was able | ||||
|     # to read it.  | ||||
|     echo_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "echo", "request") | ||||
|     echo_destination = RNS.Destination( | ||||
|         server_identity, | ||||
|         RNS.Destination.IN, | ||||
|         RNS.Destination.SINGLE, | ||||
|         APP_NAME, | ||||
|         "echo", | ||||
|         "request" | ||||
|     ) | ||||
| 
 | ||||
|     # We configure the destination to automatically prove all | ||||
|     # packets adressed to it. By doing this, RNS will automatically | ||||
| @ -54,7 +61,11 @@ def server(configpath): | ||||
| 
 | ||||
| def announceLoop(destination): | ||||
|     # Let the user know that everything is ready | ||||
|     RNS.log("Echo server "+RNS.prettyhexrep(destination.hash)+" running, hit enter to manually send an announce (Ctrl-C to quit)") | ||||
|     RNS.log( | ||||
|         "Echo server "+ | ||||
|         RNS.prettyhexrep(destination.hash)+ | ||||
|         " running, hit enter to manually send an announce (Ctrl-C to quit)" | ||||
|     ) | ||||
| 
 | ||||
|     # We enter a loop that runs until the users exits. | ||||
|     # If the user hits enter, we will announce our server | ||||
| @ -85,7 +96,10 @@ def client(destination_hexhash, configpath, timeout=None): | ||||
|     # hash that was entered on the command line | ||||
|     try: | ||||
|         if len(destination_hexhash) != 20: | ||||
|             raise ValueError("Destination length is invalid, must be 20 hexadecimal characters (10 bytes)") | ||||
|             raise ValueError( | ||||
|                 "Destination length is invalid, must be 20 hexadecimal characters (10 bytes)" | ||||
|             ) | ||||
| 
 | ||||
|         destination_hash = bytes.fromhex(destination_hexhash) | ||||
|     except: | ||||
|         RNS.log("Invalid destination entered. Check your input!\n") | ||||
| @ -100,7 +114,11 @@ def client(destination_hexhash, configpath, timeout=None): | ||||
|         RNS.loglevel = RNS.LOG_INFO | ||||
| 
 | ||||
|     # Tell the user that the client is ready! | ||||
|     RNS.log("Echo client ready, hit enter to send echo request to "+destination_hexhash+" (Ctrl-C to quit)") | ||||
|     RNS.log( | ||||
|         "Echo client ready, hit enter to send echo request to "+ | ||||
|         destination_hexhash+ | ||||
|         " (Ctrl-C to quit)" | ||||
|     ) | ||||
| 
 | ||||
|     # We enter a loop that runs until the user exits. | ||||
|     # If the user hits enter, we will try to send an | ||||
| @ -127,7 +145,14 @@ def client(destination_hexhash, configpath, timeout=None): | ||||
|             # example_utilities.echo.request | ||||
|             # This matches the naming we specified in the | ||||
|             # server part of the code. | ||||
|             request_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "echo", "request") | ||||
|             request_destination = RNS.Destination( | ||||
|                 server_identity, | ||||
|                 RNS.Destination.OUT, | ||||
|                 RNS.Destination.SINGLE, | ||||
|                 APP_NAME, | ||||
|                 "echo", | ||||
|                 "request" | ||||
|             ) | ||||
| 
 | ||||
|             # The destination is ready, so let's create a packet. | ||||
|             # We set the destination to the request_destination | ||||
| @ -172,7 +197,11 @@ def packet_delivered(receipt): | ||||
|             rtt = round(rtt*1000, 3) | ||||
|             rttstring = str(rtt)+" milliseconds" | ||||
| 
 | ||||
|         RNS.log("Valid reply received from "+RNS.prettyhexrep(receipt.destination.hash)+", round-trip time is "+rttstring) | ||||
|         RNS.log( | ||||
|             "Valid reply received from "+ | ||||
|             RNS.prettyhexrep(receipt.destination.hash)+ | ||||
|             ", round-trip time is "+rttstring | ||||
|         ) | ||||
| 
 | ||||
| # This function is called if a packet times out. | ||||
| def packet_timed_out(receipt): | ||||
| @ -190,10 +219,39 @@ def packet_timed_out(receipt): | ||||
| if __name__ == "__main__": | ||||
|     try: | ||||
|         parser = argparse.ArgumentParser(description="Simple echo server and client utility") | ||||
|         parser.add_argument("-s", "--server", action="store_true", help="wait for incoming packets from clients") | ||||
|         parser.add_argument("-t", "--timeout", action="store", metavar="s", default=None, help="set a reply timeout in seconds", type=float) | ||||
|         parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str) | ||||
|         parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the server destination", type=str) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "-s", | ||||
|             "--server", | ||||
|             action="store_true", | ||||
|             help="wait for incoming packets from clients" | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "-t", | ||||
|             "--timeout", | ||||
|             action="store", | ||||
|             metavar="s", | ||||
|             default=None, | ||||
|             help="set a reply timeout in seconds", | ||||
|             type=float | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument("--config", | ||||
|             action="store", | ||||
|             default=None, | ||||
|             help="path to alternative Reticulum config directory", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "destination", | ||||
|             nargs="?", | ||||
|             default=None, | ||||
|             help="hexadecimal hash of the server destination", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         args = parser.parse_args() | ||||
| 
 | ||||
|         if args.server: | ||||
|  | ||||
| @ -54,7 +54,14 @@ def server(configpath, path): | ||||
|     # We create a destination that clients can connect to. We | ||||
|     # want clients to create links to this destination, so we | ||||
|     # need to create a "single" destination type. | ||||
|     server_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "filetransfer", "server") | ||||
|     server_destination = RNS.Destination( | ||||
|         server_identity, | ||||
|         RNS.Destination.IN, | ||||
|         RNS.Destination.SINGLE, | ||||
|         APP_NAME, | ||||
|         "filetransfer", | ||||
|         "server" | ||||
|     ) | ||||
| 
 | ||||
|     # We configure a function that will get called every time | ||||
|     # a new client creates a link to this destination. | ||||
| @ -135,7 +142,13 @@ def client_request(message, packet): | ||||
|             # read it and pack it as a resource | ||||
|             RNS.log("Client requested \""+filename+"\"") | ||||
|             file = open(os.path.join(serve_path, filename), "rb") | ||||
|             file_resource = RNS.Resource(file, packet.link, callback=resource_sending_concluded) | ||||
|              | ||||
|             file_resource = RNS.Resource( | ||||
|                 file, | ||||
|                 packet.link, | ||||
|                 callback=resource_sending_concluded | ||||
|             ) | ||||
| 
 | ||||
|             file_resource.filename = filename | ||||
|         except Exception as e: | ||||
|             # If somethign went wrong, we close | ||||
| @ -223,7 +236,14 @@ def client(destination_hexhash, configpath): | ||||
| 
 | ||||
|     # When the server identity is known, we set | ||||
|     # up a destination | ||||
|     server_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "filetransfer", "server") | ||||
|     server_destination = RNS.Destination( | ||||
|         server_identity, | ||||
|         RNS.Destination.OUT, | ||||
|         RNS.Destination.SINGLE, | ||||
|         APP_NAME, | ||||
|         "filetransfer", | ||||
|         "server" | ||||
|     ) | ||||
| 
 | ||||
|     # We also want to automatically prove incoming packets | ||||
|     server_destination.set_proof_strategy(RNS.Destination.PROVE_ALL) | ||||
| @ -524,10 +544,34 @@ def clear_screen(): | ||||
| # starts up the desired program mode. | ||||
| if __name__ == "__main__": | ||||
|     try: | ||||
|         parser = argparse.ArgumentParser(description="Simple file transfer server and client utility") | ||||
|         parser.add_argument("-s", "--serve", action="store", metavar="dir", help="serve a directory of files to clients") | ||||
|         parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str) | ||||
|         parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the server destination", type=str) | ||||
|         parser = argparse.ArgumentParser( | ||||
|             description="Simple file transfer server and client utility" | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "-s", | ||||
|             "--serve", | ||||
|             action="store", | ||||
|             metavar="dir", | ||||
|             help="serve a directory of files to clients" | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "--config", | ||||
|             action="store", | ||||
|             default=None, | ||||
|             help="path to alternative Reticulum config directory", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "destination", | ||||
|             nargs="?", | ||||
|             default=None, | ||||
|             help="hexadecimal hash of the server destination", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         args = parser.parse_args() | ||||
| 
 | ||||
|         if args.config: | ||||
|  | ||||
| @ -34,7 +34,13 @@ def server(configpath): | ||||
|     # We create a destination that clients can connect to. We | ||||
|     # want clients to create links to this destination, so we | ||||
|     # need to create a "single" destination type. | ||||
|     server_destination = RNS.Destination(server_identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "linkexample") | ||||
|     server_destination = RNS.Destination( | ||||
|         server_identity, | ||||
|         RNS.Destination.IN, | ||||
|         RNS.Destination.SINGLE, | ||||
|         APP_NAME, | ||||
|         "linkexample" | ||||
|     ) | ||||
| 
 | ||||
|     # We configure a function that will get called every time | ||||
|     # a new client creates a link to this destination. | ||||
| @ -46,7 +52,12 @@ def server(configpath): | ||||
| 
 | ||||
| def server_loop(destination): | ||||
|     # Let the user know that everything is ready | ||||
|     RNS.log("Link example "+RNS.prettyhexrep(destination.hash)+" running, waiting for a connection.") | ||||
|     RNS.log( | ||||
|         "Link example "+ | ||||
|         RNS.prettyhexrep(destination.hash)+ | ||||
|         " running, waiting for a connection." | ||||
|     ) | ||||
| 
 | ||||
|     RNS.log("Hit enter to manually send an announce (Ctrl-C to quit)") | ||||
| 
 | ||||
|     # We enter a loop that runs until the users exits. | ||||
| @ -124,7 +135,13 @@ def client(destination_hexhash, configpath): | ||||
| 
 | ||||
|     # When the server identity is known, we set | ||||
|     # up a destination | ||||
|     server_destination = RNS.Destination(server_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, APP_NAME, "linkexample") | ||||
|     server_destination = RNS.Destination( | ||||
|         server_identity, | ||||
|         RNS.Destination.OUT, | ||||
|         RNS.Destination.SINGLE, | ||||
|         APP_NAME, | ||||
|         "linkexample" | ||||
|     ) | ||||
| 
 | ||||
|     # And create a link | ||||
|     link = RNS.Link(server_destination) | ||||
| @ -214,9 +231,30 @@ def client_packet_received(message, packet): | ||||
| if __name__ == "__main__": | ||||
|     try: | ||||
|         parser = argparse.ArgumentParser(description="Simple link example") | ||||
|         parser.add_argument("-s", "--server", action="store_true", help="wait for incoming link requests from clients") | ||||
|         parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str) | ||||
|         parser.add_argument("destination", nargs="?", default=None, help="hexadecimal hash of the server destination", type=str) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "-s", | ||||
|             "--server", | ||||
|             action="store_true", | ||||
|             help="wait for incoming link requests from clients" | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "--config", | ||||
|             action="store", | ||||
|             default=None, | ||||
|             help="path to alternative Reticulum config directory", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "destination", | ||||
|             nargs="?", | ||||
|             default=None, | ||||
|             help="hexadecimal hash of the server destination", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         args = parser.parse_args() | ||||
| 
 | ||||
|         if args.config: | ||||
|  | ||||
| @ -27,7 +27,13 @@ def program_setup(configpath): | ||||
|     # existence, which will let the network know they are reachable | ||||
|     # and autoomatically create paths to them, from anywhere else | ||||
|     # in the network. | ||||
|     destination = RNS.Destination(identity, RNS.Destination.IN, RNS.Destination.SINGLE, APP_NAME, "minimalsample") | ||||
|     destination = RNS.Destination( | ||||
|         identity, | ||||
|         RNS.Destination.IN, | ||||
|         RNS.Destination.SINGLE, | ||||
|         APP_NAME, | ||||
|         "minimalsample" | ||||
|     ) | ||||
| 
 | ||||
|     # We configure the destination to automatically prove all | ||||
|     # packets adressed to it. By doing this, RNS will automatically | ||||
| @ -44,7 +50,11 @@ def program_setup(configpath): | ||||
| 
 | ||||
| def announceLoop(destination): | ||||
|     # Let the user know that everything is ready | ||||
|     RNS.log("Minimal example "+RNS.prettyhexrep(destination.hash)+" running, hit enter to manually send an announce (Ctrl-C to quit)") | ||||
|     RNS.log( | ||||
|         "Minimal example "+ | ||||
|         RNS.prettyhexrep(destination.hash)+ | ||||
|         " running, hit enter to manually send an announce (Ctrl-C to quit)" | ||||
|     ) | ||||
| 
 | ||||
|     # We enter a loop that runs until the users exits. | ||||
|     # If the user hits enter, we will announce our server | ||||
| @ -65,8 +75,18 @@ def announceLoop(destination): | ||||
| # the desired program mode. | ||||
| if __name__ == "__main__": | ||||
|     try: | ||||
|         parser = argparse.ArgumentParser(description="Bare minimum example to start Reticulum and create a destination") | ||||
|         parser.add_argument("--config", action="store", default=None, help="path to alternative Reticulum config directory", type=str) | ||||
|         parser = argparse.ArgumentParser( | ||||
|             description="Minimal example to start Reticulum and create a destination" | ||||
|         ) | ||||
| 
 | ||||
|         parser.add_argument( | ||||
|             "--config", | ||||
|             action="store", | ||||
|             default=None, | ||||
|             help="path to alternative Reticulum config directory", | ||||
|             type=str | ||||
|         ) | ||||
| 
 | ||||
|         args = parser.parse_args() | ||||
| 
 | ||||
|         if args.config: | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user