Writing a bot¶
A bot is a program that posts by itself: release notes, a daily
picture, news from a feed. The Unseenet core has a small API for that,
unseenet.core.poster, which never asks anybody for anything.
The smallest bot¶
import asyncio
from unseenet.core.poster import Poster, load_key
GROUP = "alt.hello@<the channel's fingerprint>"
async def main():
async with Poster(load_key(), from_name="Hello bot") as p:
result = await p.post(GROUP, "Hello", "Hello from a bot.\n")
print(result.message_id, "taken by", result.accepted or "nobody")
asyncio.run(main())
Run it with the key in the environment:
UNSEENET_SEED=<hex seed of the bot's key> python hello.py
That is all: the Poster starts its own Tor, posts the same article to
every node in the built-in list of known nodes, adds the postage each
node and the group ask for, and tries again later when a node says the
article cannot be taken yet.
The key¶
Give the bot a key of its own. In a channel, the owner makes that key a poster (command palette, Make poster); the owner's own key never goes near the bot. If the bot's key leaks, the owner removes it from the channel and makes a new one.
load_key() takes the first of:
key_file=: a file with the seed in hex;vault=: an Unseenet vault, unlocked with the passphrase inUNSEENET_VAULT_PASSPHRASE(identity=picks one of its identities);UNSEENET_SEED: the seed in hex.
Tor and nodes¶
Poster(key, nodes=[...], tor=...):
nodes: addresses (xyz.onionorhost:port); the known nodes by default.tor="auto"(default): a Tor that is already running ifUNSEENET_TOR_SOCKSnames its SOCKS port, otherwise its own if any node is an onion service."own","none"or a SOCKS port as a number choose directly.
What a post looks like¶
p.post(group, subject, body, kind="post", references=None,
content_type="text/plain; charset=utf-8"):
kind="chat"posts a chat line (no subject) instead of a post (UN-RFC 0028);referencesmakes it a reply - in a channel open for comments, a comment (UN-RFC 0030).
The result says which nodes took it (accepted), which refused it and
why (refused), and which could not be reached (unreachable).
To avoid posting the same thing twice after a restart,
await p.recent_threads(group) lists the newest threads as
(Message-ID, subject), and await p.article(message_id) fetches one.
Be a good bot¶
- Say that you are a bot: publish a profile with the bot mark
(
Session.publish_profilewithmake_profile(..., bot=True), UN-RFC 0022), and name a person to contact. - Post only what you may: credit the source and link to it, as its licence asks.
- Post rarely. A channel that floods is a channel people unsubscribe from.
The project's bots¶
The project runs a few bots of its own, each with its own key and a profile that says it is a bot.
| Bot | Group | What it posts |
|---|---|---|
| Wikipedia news | alt.news.wikipedia |
one post per story in Wikipedia's In the news |
| On this day | alt.history.onthisday |
the day's anniversaries from Wikipedia |
| Global Voices | alt.news.globalvoices |
whole stories (CC BY 3.0) |
| The Conversation | alt.news.theconversation |
the opening of articles, with a link |
| EFF Deeplinks | alt.privacy.eff |
posts on digital rights and privacy |
| NASA | alt.space.nasa |
NASA's Image of the Day |
| Unseenet help | unseenet.help |
answers to questions, from the docs, issues and pull requests (Getting help) |
The news bots post in channels: only the bot starts threads, and anyone
may comment on a post. They are built on unseenet.core.poster and live,
with a runner you can use for your own, in
unseenet/unseenet-bots.
The help bot is different: it reads a group and answers in its threads,
so it is built on the client's Session, like the terminal and web
clients. Its code, with an install script for a server, is
unseenet/unseenet-helpbot.