Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add nodejs jsonrpc over stdio with @deltachat/stdio-rpc-server #69

Merged
merged 3 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ An echo bot in multiple languages to get you started.

### Direct:

| Language | core version |
| ------------------------------------------------------------ | ---------------------------------------------------------------------------- |
| [C](./c) | `1.132.1` |
| [Go](./go) | `1.127.0` (jsonrpc, newer core might work too, last tested with `v1.131.4` ) |
| [node.js over cffi](./nodejs_cffi) | `1.132.1` |
| [node.js over jsonrpc](./nodejs_napi_jsonrpc) (unmaintained) | `1.101.0` |
| [Python over cffi](./python_cffi) | `1.132.1` |
| [Python over jsonrpc](./python_jsonrpc) | `1.132.1` |
| [Rust](./rust) | `1.132.1` |
| Language | core version |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| [C](./c) | `1.132.1` |
| [Go](./go) | `1.127.0` (jsonrpc, newer core might work too, last tested with `v1.131.4` ) |
| [node.js over cffi napi](./nodejs_cffi) | `1.132.1` |
| [node.js over jsonrpc napi](./nodejs_napi_jsonrpc) (unmaintained) | `1.101.0` |
| [node.js over jsonrpc stdio](./nodejs_stdio_jsonrpc) | `1.137.4` |
| [Python over cffi](./python_cffi) | `1.132.1` |
| [Python over jsonrpc](./python_jsonrpc) | `1.132.1` |
| [Rust](./rust) | `1.132.1` |

### With abstraction layer / bot framework:

Expand Down
2 changes: 1 addition & 1 deletion nodejs_napi_jsonrpc/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Echo Bot - Nodejs over jsonrpc api
# Echo Bot - Nodejs over jsonrpc api over napi-rs

jsonrpc is the new way to speak with our core library, it is faster (both to use and to develop) and returns better errors than the cffi.

Expand Down
2 changes: 2 additions & 0 deletions nodejs_stdio_jsonrpc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
deltachat-data
80 changes: 80 additions & 0 deletions nodejs_stdio_jsonrpc/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Echo Bot - Nodejs over jsonrpc api over stdio

jsonrpc is the new way to speak with our core library, it is faster (both to use and to develop) and returns better errors than the cffi.
This example uses the new reproducible deltachat-rpc-server binary and comunicates with it over stdio pipes.

## Run the bot:

you need nodejs version `>=18`.
install dependencies with `npm install`.

Set your credentials as enviroment variables:

```sh
# on Mac and Linux
export ADDR=$yourEmail
export MAIL_PW=$yourPassword
# on windows
## TODO
```

then start with:

```
node .
```

on linux and mac you can also do it in one line:

```
ADDR=$yourEmail MAIL_PW=$yourPassword node .
```

> you only need to provide the email credentials the first time you start it, they won't have an effect after the account is already configured.

#### NEW! get a quick chatmail account

Chatmail is a server configuration optimized for DeltaChat.

- advantages:
- fast testing / super quick sign up without personal data
- very fast
- disadvantage:
- forces you to be on the same chatmail instance, unless you verify the bots contact with the the vergification code.


To set-up an account via chatmail:
```
CHATMAIL_QR=dcaccount:https://nine.testrun.org/new node .
```

### Useful Links

- Generated client code for the jsonrpc: https://github.com/deltachat/deltachat-core-rust/blob/master/deltachat-jsonrpc/typescript/generated/client.ts
- Generated typescript types for the jsonrpc: https://github.com/deltachat/deltachat-core-rust/blob/master/deltachat-jsonrpc/typescript/generated/types.ts
- Sourcecode of the jsonrpc client: https://github.com/deltachat/deltachat-core-rust/blob/master/deltachat-jsonrpc/typescript/
- Sourcecode of the deltachat-rpc-server to javascript bindings: https://github.com/deltachat/deltachat-core-rust/blob/master/deltachat-rpc-server/npm-package

### Experimental: Usage with deno instead of node:

Deno (https://deno.com/) is an alternative to nodejs that is better than nodejs in some areas,
such as typescript support out of the box, good default tooling, and a strict focus on security through it's permission system.

For deno the native prebuilds in the npm package do not work (they do not get installed / found),
so you need to install `deltachat-rpc-server` yourself to `$PATH` with:
```sh
cargo install --git https://github.com/deltachat/deltachat-core-rust deltachat-rpc-server
```
Or you download it from the releases page on https://github.com/deltachat/deltachat-core-rust/releases/
and point to it with the `DELTA_CHAT_RPC_SERVER` environment variable:
```sh
# make it executable
chmod +x ./deltachat-rpc-server-aarch64-macos
# set the `DELTA_CHAT_RPC_SERVER` environment variable
export DELTA_CHAT_RPC_SERVER=./deltachat-rpc-server-aarch64-macos
```

run `index_deno.js`:
```sh
CHATMAIL_QR=dcaccount:https://nine.testrun.org/new deno run --allow-env --allow-read --allow-run=deltachat-rpc-server index_deno.js
```
84 changes: 84 additions & 0 deletions nodejs_stdio_jsonrpc/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//@ts-check

import { startDeltaChat } from "@deltachat/stdio-rpc-server";
import { C } from "@deltachat/jsonrpc-client";

async function main() {
const dc = await startDeltaChat("deltachat-data");
console.log("Using deltachat-rpc-server at " + dc.pathToServerBinary);

// log all events to console
// dc.on("ALL", console.debug.bind("[core]"));

// or only log what you want
dc.on("Info", (accountId, { msg }) =>
console.info(accountId, "[core:info]", msg)
);
dc.on("Warning", (accountId, { msg }) =>
console.warn(accountId, "[core:warn]", msg)
);
dc.on("Error", (accountId, { msg }) =>
console.error(accountId, "[core:error]", msg)
);

let firstAccount = (await dc.rpc.getAllAccounts())[0];
if (!firstAccount) {
firstAccount = await dc.rpc.getAccountInfo(await dc.rpc.addAccount());
}
if (firstAccount.kind === "Unconfigured") {
console.info("account not configured, trying to login now...");
try {
if (!!process.env.ADDR && !!process.env.MAIL_PW) {
await dc.rpc.batchSetConfig(firstAccount.id, {
addr: process.env.ADDR,
mail_pw: process.env.MAIL_PW,
});
} else if (!!process.env.CHATMAIL_QR) {
await dc.rpc.setConfigFromQr(firstAccount.id, process.env.CHATMAIL_QR);
} else {
throw new Error(
"Credentials missing, you need to set ADDR and MAIL_PW"
);
}
await dc.rpc.batchSetConfig(firstAccount.id, {
bot: "1",
e2ee_enabled: "1",
});
await dc.rpc.configure(firstAccount.id);
} catch (error) {
console.error("Could not log in to account:", error);
process.exit(1);
}
} else {
await dc.rpc.startIo(firstAccount.id);
}

const botAccountId = firstAccount.id;
const emitter = dc.getContextEvents(botAccountId);
emitter.on("IncomingMsg", async ({ chatId, msgId }) => {
const chat = await dc.rpc.getBasicChatInfo(botAccountId, chatId);
// only echo to DM chat
if (chat.chatType === C.DC_CHAT_TYPE_SINGLE) {
const message = await dc.rpc.getMessage(botAccountId, msgId);
await dc.rpc.miscSendTextMessage(
botAccountId,
chatId,
message.text || ""
);
}
});

const botAddress = await dc.rpc.getConfig(botAccountId, "addr");
const verificationQRCode = (
await dc.rpc.getChatSecurejoinQrCodeSvg(botAccountId, null)
)[0];
console.info("".padEnd(40, "="))
console.info("The email address of your bot is: ", botAddress);
console.info(`Verify Bot contact (if you use chatmail this is nessesary to contact the bot from outside the chatmail instance that the bot uses):
copy this code and \"scan\" it with delta chat:

${verificationQRCode}`);
console.info("".padEnd(40, "="))
}

main();
85 changes: 85 additions & 0 deletions nodejs_stdio_jsonrpc/index_deno.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//@ts-check

import { startDeltaChat } from "https://raw.githubusercontent.com/deltachat/deltachat-core-rust/simon/stdio-jsonrpc-server-npm-package/deltachat-rpc-server/npm-package/index.js";
import { C } from "@deltachat/jsonrpc-client";
import process from 'node:process'

async function main() {
const dc = await startDeltaChat("deltachat-data");
console.log("Using deltachat-rpc-server at " + dc.pathToServerBinary);

// log all events to console
// dc.on("ALL", console.debug.bind("[core]"));

// or only log what you want
dc.on("Info", (accountId, { msg }) =>
console.info(accountId, "[core:info]", msg)
);
dc.on("Warning", (accountId, { msg }) =>
console.warn(accountId, "[core:warn]", msg)
);
dc.on("Error", (accountId, { msg }) =>
console.error(accountId, "[core:error]", msg)
);

let firstAccount = (await dc.rpc.getAllAccounts())[0];
if (!firstAccount) {
firstAccount = await dc.rpc.getAccountInfo(await dc.rpc.addAccount());
}
if (firstAccount.kind === "Unconfigured") {
console.info("account not configured, trying to login now...");
try {
if (!!process.env.ADDR && !!process.env.MAIL_PW) {
await dc.rpc.batchSetConfig(firstAccount.id, {
addr: process.env.ADDR,
mail_pw: process.env.MAIL_PW,
});
} else if (!!process.env.CHATMAIL_QR) {
await dc.rpc.setConfigFromQr(firstAccount.id, process.env.CHATMAIL_QR);
} else {
throw new Error(
"Credentials missing, you need to set ADDR and MAIL_PW"
);
}
await dc.rpc.batchSetConfig(firstAccount.id, {
bot: "1",
e2ee_enabled: "1",
});
await dc.rpc.configure(firstAccount.id);
} catch (error) {
console.error("Could not log in to account:", error);
process.exit(1);
}
} else {
await dc.rpc.startIo(firstAccount.id);
}

const botAccountId = firstAccount.id;
const emitter = dc.getContextEvents(botAccountId);
emitter.on("IncomingMsg", async ({ chatId, msgId }) => {
const chat = await dc.rpc.getBasicChatInfo(botAccountId, chatId);
// only echo to DM chat
if (chat.chatType === C.DC_CHAT_TYPE_SINGLE) {
const message = await dc.rpc.getMessage(botAccountId, msgId);
await dc.rpc.miscSendTextMessage(
botAccountId,
chatId,
message.text || ""
);
}
});

const botAddress = await dc.rpc.getConfig(botAccountId, "addr");
const verificationQRCode = (
await dc.rpc.getChatSecurejoinQrCodeSvg(botAccountId, null)
)[0];
console.info("".padEnd(40, "="))
console.info("The email address of your bot is: ", botAddress);
console.info(`Verify Bot contact (if you use chatmail this is nessesary to contact the bot from outside the chatmail instance that the bot uses):
copy this code and \"scan\" it with delta chat:

${verificationQRCode}`);
console.info("".padEnd(40, "="))
}

main();
Loading