Quick start — Wasm
A Wasm plugin is a wasm32 module with a few exports and one import. Rust and C have ready SDKs in the repository; any other language does the same following the Wasm ABI.
Manifest
Section titled “Manifest”{ "schema": 1, "id": "com.example.counter", "name": "Counter", "version": "1.0.0", "description": "Counts the messages it sees.", "author": { "name": "Example Dev" }, "license": "MIT", "compat": { "freshgram": ">=7.0.0 <8.0.0", "api": 7 }, "runtime": { "kind": "wasm", "module": "counter.wasm", "language": "rust" }, "permissions": { "required": [ { "id": "messages.read", "reason": "Counts the messages it sees." }, { "id": "storage.private", "reason": "Remembers the count." } ] }, "contributes": { "events": ["message.new"] }}The plugin’s Cargo.toml is a cdylib library that depends on the freshgram crate (from Telegram/plugins_sdk/rust):
[lib]crate-type = ["cdylib"]
[dependencies]freshgram = { path = "../.." }
[profile.release]opt-level = "s"lto = truepanic = "abort"strip = truesrc/lib.rs:
use freshgram::{log, storage_get, storage_set, Value};
fn init(_info: &Value) -> i32 { let _ = log("counter ready"); 0}
fn event(event: &Value) -> i32 { let chat = event.get("chat").and_then(Value::as_i64).unwrap_or(0); let count = match storage_get("count") { Ok(value) => value.and_then(|value| value.as_i64()).unwrap_or(0) + 1, Err(error) => { let _ = log("cannot read the counter"); return 100 + error.kind as i32 + 1; } }; if let Err(error) = storage_set("count", count) { let _ = log("cannot save the counter"); return 200 + error.kind as i32 + 1; } let _ = storage_set("last_chat", chat); count as i32}
freshgram::plugin!(init = init, event = event);Build:
cargo build --release --target wasm32-unknown-unknownPut the resulting target/wasm32-unknown-unknown/release/counter.wasm next to manifest.json.
#include "freshgram.h"
int fg_plugin_init(fg_value info) { (void)info; fg_log("counter ready"); return 0;}
int fg_plugin_event(fg_value event) { fg_i64 chat = 0; fg_as_int(fg_find(event, "chat"), &chat); fg_i64 count = 0; int read = fg_storage_get_int("count", &count); if (read != FG_OK) { fg_log("cannot read the counter"); return 100 + read; } count += 1; int written = fg_storage_set_int("count", count); if (written != FG_OK) { fg_log("cannot save the counter"); return 200 + written; } fg_storage_set_int("last_chat", chat); return (int)count;}Build (the header is self-contained, no libc; the host tests build the example the same way):
clang --target=wasm32 -O2 -nostdlib -fno-builtin -Wl,--no-entry -o counter.wasm counter.cThe freshgram.call import is declared in the header with import_module/import_name attributes, so no extra linker flags are needed.
Return value
Section titled “Return value”Both init and the event handler return an int. 0 means success; a non-zero value is recorded by the host as the status of the handling. The example above returns the counter, which is allowed, but real plugins should return 0 on success and a negative number on error.
The numbers in the examples are only illustrative. In Rust, error.kind as i32 is the position of the variant in the ErrorKind enum (Denied is 0 … Cancelled is 7, Transport is 8), so the Rust example returns 109 for Transport and 108 for Cancelled; these are not the FG_* codes of the C header. The host does not interpret the number: for ordinary events the status is ignored, for init a non-zero status only writes a line to the plugin log, and only for message hooks does a non-zero status discard the plugin’s edit. See Return values.
