đ©âđ» DĂ©veloppement#
Nous allons essayer dâexpliquer le fonctionnement et lâutilisation de notre paquet npm @olvid/bot-node. IdĂ©alement, il vaut mieux la suivre Ă©tape par Ă©tape afin de suivre le cheminement, mais si vous vous sentez Ă lâaise, vous pouvez essayer dâaccĂ©der directement Ă la section qui vous intĂ©resse.
Ă propos de @olvid/bot-web
Le paquet @olvid/bot-web est trĂšs similaire dans son usage mais possĂšde certaines contraintes. (voir đ Navigateur)
il nâutilise pas gRPC directement, il doit donc utiliser un proxy cf: Daemon et proxy
il nâa pas accĂšs Ă lâenvironnement, il faut donc passer lâurl du serveur et la clĂ© client lors de la crĂ©ation dâun OlvidClient (ou la passer par dâautre moyens)
lâenvoi de fichier ne fonctionne pas donc certaines mĂ©thodes comme sendMessageWithAttachments ne sont pas implĂ©mentĂ©es.
Vous pouvez facilement adapter le code présent dans cette page, mais il ne fonctionnera pas tel quel.
Les bases#
OlvidClient#
Pour interagir avec le daemon, il vous faudra systĂ©matiquement crĂ©er une instance de la classe OlvidClient. Il peut sâagir de la classe dâorigine ou dâune classe enfant.
En node cette classe va automatiquement rĂ©cupĂ©rer une clĂ© client en utilisant lâenvironnement ou un fichier .env. (cf. Configuration)
Grùce à ce client, on pourra notamment exécuter des commandes et réagir à des notifications.
Voici Ă quoi ressemble un fichier main.ts de base contenant la crĂ©ation dâun client Olvid et lâexĂ©cution dâune commande (afficher lâidentitĂ© courante).
Cette structure est Ă utiliser dans chacun des exemples de code de cette page, il suffit de remplacer le contenu de la fonction main par le code de votre choix.
import { OlvidClient, datatypes } from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
const identity: datatypes.Identity = await client.identityGet();
console.log(identity)
}
main().then();
Commande#
Toutes les méthodes gRPC de commandes exposées par le daemon sont facilement accessibles grùce à des méthodes de la classe OlvidClient.
Par exemple, pour envoyer un message (mĂ©thode MessageSend en gRPC), on utilisera la mĂ©thode messageSend de notre instance dâOlvidClient.
Dans le cas oĂč nous connaissons lâidentifiant de la discussion dans laquelle poster, cela donnerait :
import { OlvidClient, datatypes } from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
await client.messageSend({discussionId: 1, body: "Use Olvid !"});
console.log(identity)
}
main().then();
Notification#
La classe OlvidClient permet de facilement écouter les notifications émises par le daemon.
Ces mĂ©thodes commencent toutes par le prĂ©fixe on et permettent dâajouter une fonction de rappel qui sera executĂ© Ă chaque nouvelle notification.
Par exemple, pour afficher dans le terminal quand un message est reçu et lorsquâune rĂ©action est ajoutĂ©e, on peut faire :
import { OlvidClient, datatypes } from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
client.onMessageReceived({
callback: (message: datatypes.Message) => {
console.log("Message received:", message.body);
}
})
client.onMessageReactionAdded({
callback: (message: datatypes.Message, reaction: datatypes.MessageReaction) => {
console.log("Reaction added:", reaction.reaction);
}
})
await client.runForever();
}
main().then();
Utilisation avancée#
Filtrage des éléments#
Pour accéder à des éléments précis de la base donnée on peut appliquer un filtre lors des commandes de type List.
Voici des exemples de filtres possibles, les diffĂ©rents filtres sont dĂ©finis dans la description protobuf de lâAPI du daemon (ici).
import {datatypes, OlvidClient} from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
/*
** list one to one discussions
*/
let otoDiscussionFilter = new datatypes.DiscussionFilter({
type: datatypes.DiscussionFilter_Type.OTO
});
for await (const discussion of client.discussionList({filter: otoDiscussionFilter})) {
console.log(`OneToOne discussion: ${discussion.id} - ${discussion.title}`)
}
/*
** list messages in a specific discussion with reactions
*/
const messageFilter = new datatypes.MessageFilter({
discussionId: 1n,
hasReaction: datatypes.MessageFilter_Reaction.HAS
})
const filteredMessages = await Array.fromAsync(client.messageList({filter: messageFilter}));
console.log("Message with reactions in discussion", filteredMessages)
}
main().then();
Filtrage des notifications#
Pour une implĂ©mentation plus fine de lâĂ©coute des notifications, on peut utiliser un systĂšme de filtrage. Les diffĂ©rentes formes de filtrage dĂ©pendent du type de notification et sont dĂ©finis dans les messages du type MessageReceivedNotificationSubscription (cf description protobuf de lâAPI du daemon).
count#
Toutes les notifications peuvent ĂȘtre programmĂ©es pour nâĂȘtre reçu quâun nombre dĂ©fini de fois Ă lâaide de lâargument count.
filter#
La plupart des notifications possĂšdent un ou plusieurs attributs filter. Si un filtre est spĂ©cifiĂ© une notification doit correspondre Ă lâensemble des critĂšres renseignĂ©s pour ĂȘtre envoyĂ©e.
Exemple#
Par exemple, on peut rĂ©pondre uniquement au prochain message qui contient hello puis quitter le programme. Pour cela, on construit un objet de type MessageFilter qui nâacceptera que les messages contenant le terme « hello » (bodySearch), et on spĂ©cifie quâon ne veut traiter quâune seule notification (count Ă 1).
import { OlvidClient, datatypes, tools } from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
client.onMessageReceived({
callback: async (message: datatypes.Message) => {
await client.messageSend({
discussionId: message.discussionId,
body: "Hello World !",
})
},
filter: new datatypes.MessageFilter({bodySearch: "hello"}),
count: 1n
})
await client.waitForCallbacksEnd();
}
main().then();
Astuce
Dans les filtres, les champs de type string se terminant en search sont interprétés par un moteur de regexp pour une plus grande polyvalence.
Raccourcis#
Les classes de base du module datatypes (Message, Discussion, Attachment, âŠ) implĂ©mentent des mĂ©thodes de raccourci pour avoir un code plus condensĂ© et agrĂ©able Ă Ă©crire.
Voici des exemples de méthodes, la liste exhaustive est définie dans le code source des classes, ou est accessible ici.
import { OlvidClient, datatypes, tools } from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
client.onMessageReceived({
callback: async (message: datatypes.Message) => {
// easily reply to a message, and access to it's sender details
const senderContact: datatypes.Contact = (await message.getSenderContact(client))
await message.reply(
client,
`Hello ${senderContact.displayName} ! đ`
);
// save attachments on disk if there are some
if (message.attachmentsCount) {
(await message.getAttachments(client)).forEach((attachment) => {
attachment.save(client, "./attachments")
})
}
}
})
// post a message in a discussion and wait for message to be uploaded on server
const discussion = (await Array.fromAsync(client.discussionList()))[0]
const sentMessage = await discussion.postMessage(client, "Hello there");
await sentMessage.waitFor.messageToBeUploaded(client);
await client.runForever();
}
main().then();
Décorateurs#
Nous avons mis en place des dĂ©corateurs qui permettent de facilement Ă©couter des notifications et mettre en place des commandes pour ChatBot. Ils permettent dâĂ©crire du code plus joli pour les programmes de type Bot qui tournent Ă lâinfini.
Pour les utiliser, il faut sous-classer la classe OlvidClient, comme dans lâexemple suivant.
import { OlvidClient, datatypes, onDiscussionNew, command } from "@olvid/bot-node";
class MyBot extends OlvidClient {
@onDiscussionNew()
async discussionNew(discussion: datatypes.Discussion) {
await discussion.postMessage(this, `Hello ${discussion.title}} !`)
// post help message when a new discussion is created
await discussion.postMessage(this, this.getHelpMessage())
}
@command("!help")
async help(message: datatypes.Message) {
await message.reply(this, this.getHelpMessage())
}
@command("!ping")
async ping(message: datatypes.Message) {
await message.reply(this, "pong")
}
getHelpMessage() {
return "### Available commands\n- *!help*\n- *!ping*"
}
}
async function main() {
const bot = new MyBot();
await bot.runForever();
}
main().then();
Conseils et astuces#
Invitations#
Pour rendre plus facile la mise en relation avec un bot, il est possible dâactiver lâacceptation automatique des invitations. Il suffit de modifier la configuration du daemon Ă lâaide la mĂ©thode enableAutoInvitation dâun client Olvid.
Note
Lâacceptation automatique des invitations ne peut accepter que les prĂ©sentations, les invitations de groupe et les invitations Ă une discussion personnelle. Il ne peut pas aller au bout des invitations directes avec Ă©change de SAS code.
Voici un programme qui lance un bot aprĂšs avoir configurĂ© lâacceptation automatique des invitations.
import { OlvidClient, datatypes, tools } from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
client.onMessageReceived({
callback: (message: datatypes.Message) => {
console.log("Message received:", message.body);
}
})
await client.enableAutoInvitation({acceptAll: true});
await client.runForever();
}
main().then();
Nettoyage des messages#
Pour des raisons de performances et de confidentialitĂ© nous vous conseillons dâactiver le nettoyage automatique des messages.
Il est possible de limiter le nombre de messages conservĂ©s globalement ou par discussion (global_count et discussion_count) et/ou de dĂ©finir lâĂąge maximal dâun message (existence_duration).
Nous vous conseillons aussi dâactiver la suppression des messages dans les discussions fermĂ©es.
import { OlvidClient, datatypes, tools } from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
// keep messages up to 7 days,
// with a maximum of 100 messages and 20 messages per discussions,
// and deletes messages when a discussion is locked
await client.setMessageRetentionPolicy({
globalCount: 100n, discussionCount: 20n,
existenceDuration: 60n*60n*24n*7n, cleanLockedDiscussions: true
});
}
main().then();
Divers#
Envoyer un message éphémÚre#
Les points dâentrĂ©e API messageSend et messageSendWithAttachments permettent de spĂ©cifier lâĂ©phĂ©mĂ©ralitĂ© du message Ă envoyer.
On utilisera pour cela lâobjet datatypes.MessageEphemerality.
Voici un exemple en typescript. Il est possible de spécifier les paramÚtres read_once, visibility_duration et existence_duration de maniÚre indépendante.
Les durĂ©es dâexistence et de visibilitĂ© sont en secondes.
import { OlvidClient, datatypes, tools } from "@olvid/bot-node";
async function main() {
const client = new OlvidClient();
for await (const discussion of client.discussionList()) {
await client.messageSend({
discussionId: discussion.id,
body: "Self destruct-message",
ephemerality: new datatypes.MessageEphemerality({
visibilityDuration: 10n,
existenceDuration: 60n,
readOnce: true
})
})
}
}
main().then();