π©βπ« Tutorials#
In this page, we try to explain how to perform a number of actions with our Python library. Ideally, it is best to follow it step by step in order to understand the process, but if you feel comfortable, you can try to access directly to the section that interests you.
The basics#
OlvidClient#
To interact with the daemon, you will need to systematically create an instance of the OlvidClient class. This can be the original class or a subclass.
This class will automatically retrieve a client key using the environment or a .env file. (see Python Client Configuration)
Thanks to this client, you will be able to execute commands and set up notification listeners.
Hereβs what a basic main.py file looks like containing the creation of an Olvid client and executing a command (display current identity). This structure should be used in each of the code examples on this page, simply replace the content of the main function with your chosen code.
import asyncio
from olvid import OlvidClient, datatypes
async def main():
client = OlvidClient()
# code to replace
print(await client.identity_get())
asyncio.set_event_loop(asyncio.new_event_loop())
asyncio.get_event_loop().run_until_complete(main())
Command#
All command gRPC methods exposed by the daemon are easily accessible through methods of the OlvidClient class. For example, to send a message (gRPC method MessageSend), we will use the message_send method of our OlvidClient instance.
In the case where we know the identifier of the discussion in which to post, this would give:
from olvid import OlvidClient
client = OlvidClient()
client.message_send(discussion_id=1, body="Use Olvid !")
Notification#
The OlvidClient class also implements methods that allow you to easily listen for notifications emitted by the daemon. These methods all start with the on_ prefix and must be overridden in a subclass of OlvidClient.
For example, to display in the terminal when a message is received and when a reaction is added, you can do:
import asyncio
from olvid import OlvidClient, datatypes
class Bot(OlvidClient):
async def on_message_received(self, message: datatypes.Message):
print(f"Message received: {message.body}")
async def on_message_reaction_added(self, message: datatypes.Message, reaction: datatypes.MessageReaction):
print(f"Reaction added: {reaction.reaction}")
async def main():
bot = Bot()
await bot.run_forever()
asyncio.set_event_loop(asyncio.new_event_loop())
asyncio.get_event_loop().run_until_complete(main())
Tips and Tricks#
AutoInvitationBot#
To make it easier to connect with a bot, it is possible to set up a bot that will accept all received invitations.
Just create an AutoInvitationBot instance from the module olvid.tools. It will automatically register to receive new invitation notifications and accept them.
Note
An AutoInvitationBot can only accept introductions and group invitations. It cannot automatically accept direct invitations with SAS code exchange.
Here is a program that launches an instance of AutoInvitationBot in the background. It is possible to launch several bots in parallel.
import asyncio
from olvid import OlvidClient, datatypes, tools
class Bot(OlvidClient):
async def on_message_received(self, message: datatypes.Message):
print(f"Message received: {message.body}")
async def main():
bot = Bot()
tools.AutoInvitationBot()
await bot.run_forever()
asyncio.set_event_loop(asyncio.new_event_loop())
asyncio.get_event_loop().run_until_complete(main())
Various#
Send an ephemeral message#
The API entry points messageSend and messageSendWithAttachments allow you to specify the ephemerality of the message to be sent. To do this, use the olvid.datatypes.MessageEphemerality object.
Here is an example in Python. It is possible to specify the parameters read_once, visibility_duration, and existence_duration independently. The existence and visibility durations are in seconds.
import asyncio
from olvid import datatypes, OlvidClient
async def main():
client = OlvidClient()
async for discussion in client.discussion_list():
await client.message_send(
discussion_id=discussion.id,
body="Self-destruct message",
ephemerality=datatypes.MessageEphemerality(
visibility_duration=10,
existence_duration=60,
read_once=True
)
)
asyncio.run(main())
Advanced Usage#
Listener#
For a more granular implementation of notifications listening, it is possible to use the concept of Listener. A listener is a subscription of a callback function to a notification type. This function will be called each time a notification of this type is received.
Each type of notification has its own class in the module olvid.listeners.
In this example, the reply_to_message method will be called every time a message arrives.
import asyncio
from olvid import OlvidClient, datatypes, listeners
async def reply_to_message(message: datatypes.Message):
await message.reply(f"Reply to: {message.body}")
async def main():
client = OlvidClient()
listener = listeners.MessageReceivedListener(handler=reply_to_message)
client.add_listener(listener)
await client.run_forever()
asyncio.set_event_loop(asyncio.new_event_loop())
asyncio.get_event_loop().run_until_complete(main())
Listener: expiration#
By default, a listener listens to notifications indefinitely, but it is possible to use the count argument to only listen for a certain number of notifications. In this case, once count notifications have been processed, the listener is stopped.
In this example, we respond to the next received message and then the program stops.
import asyncio
from olvid import OlvidClient, datatypes, listeners
async def reply_to_message(message: datatypes.Message):
await message.reply(f"Reply to: {message.body}")
async def main():
client = OlvidClient()
# added count parameter set to 1
listener = listeners.MessageReceivedListener(handler=reply_to_message, count=1)
client.add_listener(listener)
# wait_for_listeners_end: returns when all listeners are finished
await client.wait_for_listeners_end()
print("Program end")
asyncio.set_event_loop(asyncio.new_event_loop())
asyncio.get_event_loop().run_until_complete(main())
Listener: Filtering#
Listeners also allow filtering of notifications to be processed. To do this, we can add one or more filter functions to our listener. The different forms of filtering depend on the notification type and are defined in the messages of the MessageReceivedNotificationSubscription type (cf protobuf description of the daemonβs API).
For example, we might want to process only messages sent by a specific contact.
import asyncio
from olvid import OlvidClient, datatypes, listeners
CONTACT_ID: int = 1
async def reply_to_message(message: datatypes.Message):
await message.reply(f"Reply to: {message.body}")
async def main():
client = OlvidClient()
# only notifications matching check_sender_id will be handled
listener = listeners.MessageReceivedListener(handler=reply_to_message, filter=datatypes.MessageFilter(sender_contact_id=CONTACT_ID))
client.add_listener(listener)
await client.wait_for_listeners_end()
asyncio.set_event_loop(asyncio.new_event_loop())
asyncio.get_event_loop().run_until_complete(main())
Advanced listener#
It is perfectly possible to combine filtering and expiration.
Here, it is used to perform an action and exit the program when the message that has just been sent arrives on the recipientβs phone.
import asyncio
from olvid import OlvidClient, datatypes, listeners
DISCUSSION_ID: int = 1
def get_message_id_checker(message_id: datatypes.MessageId):
def checker(message: datatypes.Message):
return message.sender_id == message_id
return checker
async def main():
client = OlvidClient()
# send a message
message = await client.message_send(discussion_id=DISCUSSION_ID, body="Hello there !")
# add a listener to do something when message had been delivered, then program will exit
listener = listeners.MessageDeliveredListener(
handler=lambda m: print("Message delivered"),
checkers=[get_message_id_checker(message.id)],
count=1
)
client.add_listener(listener)
await client.wait_for_listeners_end()
print("Program end")
asyncio.set_event_loop(asyncio.new_event_loop())
asyncio.get_event_loop().run_until_complete(main())