Skip to content

Quick start — Python

hello/
├── manifest.json
└── main.py
{
"schema": 1,
"id": "com.example.hello",
"name": "Hello",
"version": "1.0.0",
"description": "Greets, counts messages and shows a settings page.",
"author": { "name": "Example Dev" },
"license": "MIT",
"compat": { "freshgram": ">=7.0.0 <8.0.0", "api": 6 },
"runtime": { "kind": "python", "entry": "main.py" },
"permissions": {
"required": [
{ "id": "messages.read", "reason": "Counts the messages it sees." },
{ "id": "messages.send", "reason": "Answers the /hello command." },
{ "id": "storage.private", "reason": "Remembers the count." },
{ "id": "ui.command", "reason": "Adds the /hello command." },
{ "id": "ui.toast", "reason": "Shows short notices." }
]
},
"contributes": {
"events": ["message.new"],
"commands": [{ "name": "hello", "description": "Says hello" }]
}
}
import freshgram
@freshgram.on("message.new")
def counted(event):
count = freshgram.storage.get("count", 0) + 1
freshgram.storage.set("count", count)
print("message", count, "in chat", event["chat"])
@freshgram.on_command("hello")
def hello(event):
count = freshgram.storage.get("count", 0)
chat = event.get("chat") # missing when the chat is outside the scope you were granted
if chat is None:
freshgram.ui.toast("Open a chat in an allowed scope first")
return
freshgram.messages.send(chat, "Hello! I have seen %d messages." % count)
freshgram.ui.toast("Said hello")
freshgram.run()

Key points:

  • @freshgram.on("message.new") subscribes a function to an event. The event must also be declared in contributes.events, and receiving it needs the messages.read permission.
  • @freshgram.on_command("hello") handles the /hello command; it is declared in contributes.commands and needs the ui.command permission.
  • A command event carries chat only while the chat is inside the scope the user granted, so the handler reads it with event.get("chat"). message.new always has chat.
  • freshgram.run() at the end is required: it starts the event loop.
  • Every call is checked against permissions. Without the permission you get a freshgram.Denied exception.

The host sources include a development mode (fgpl::dev::Run, dev_runner.h): it runs a directory with manifest.json or a .fgpl package against a small fake app. Actions can be typed line by line (REPL) or read from a script file; permissions can be granted and denied (grant/deny) and state kept between runs (state). This lets you debug a plugin without installing it in the client.