The assistant in chat rooms¶
Lens's assistant can answer in Slack, Discord, Telegram, Matrix, Mattermost, IRC, WhatsApp and the other chats Matterbridge bridges. Say "Lens, what did we decide about the launch?" (or "@Lens …") in a bridged room and it answers there, as it would in Chat.
Telegram, built in¶
Telegram needs no Matterbridge and no public address: Lens reads its own bot's messages (long polling) and answers.
- In Telegram, message @BotFather, send
/newbotand copy the token it gives you. - Open Settings → Telegram, paste the token and turn on Answer in Telegram.
- Message your bot. You show on the settings page under Wrote to the bot, not let in: click Let in.
Anyone can find a bot, so it answers only the people in People it talks to (Telegram user ids or usernames);
anyone else who writes to it is told once that they need to be let in, and shows on the page. A private chat with
the bot is always for Lens; in a group it answers what names it ("Lens, …"), mentions the bot (@your_bot) or
replies to it. Voice notes are transcribed by the server's speech-to-text (the answer starts with what it heard), and
files sent to it (up to Telegram's 20 MB for bots) are held for the assistant, which imports them into a namespace as
a file dropped into Chat is. The page lists the chats it has seen with their ids, so a group can be given to a
namespace's assistant with Chats for a namespace's assistant (-1001234567890 = pods). Approving by replying
"yes", the account it answers as and routing between namespaces work as below.
Give Lens a bot of its own: a bot that Matterbridge also reads answers neither reliably (Check the bot says so).
Telegram is one adapter of the chat-channel layer (app/domain/channels.py) that Matterbridge is another of; a new
chat app adds an adapter (reading, sending, files) and gets everything in a room for free. The assistant's own email
address is one too (The assistant by email).
Slack, built in¶
Slack needs no Matterbridge and no public address either: Lens connects to a Slack app of your own over Socket Mode and answers there.
- Open Settings → Slack and click Create the Slack app. Slack opens with the app already described (Lens's
manifest: its permissions, the
/lenscommand, Socket Mode); pick the workspace and create it. - In the app's page, Install to Workspace, then copy the Bot User OAuth Token (
xoxb-…, under OAuth & Permissions). Under Basic Information → App-Level Tokens, generate one withconnections:writeand copy it (xapp-…). - Paste both tokens in Settings → Slack and turn on Answer in Slack. Check the app says whether both work.
Message the app directly (always for Lens), invite it to a channel with /invite @Lens and say "Lens, …" or
@mention it, or type /lens what did we decide about the launch? anywhere (the answer shows in that channel).
/lens clear and /lens compact are the chat commands. In a thread it answers in the thread. Voice
clips are transcribed by the server's speech-to-text and files posted with a message are held for the assistant to
import, as with Telegram. People it talks to (Slack usernames or member ids) limits who it answers, and lets them
approve by replying "yes"; Channels for a namespace's assistant gives a channel to a namespace (slack/general =
pods). Slack is an adapter of the same chat-channel layer as Telegram and Matterbridge (app/domain/slack.py).
Give Lens an app of its own: an app whose bot token Matterbridge also uses answers twice (Check the app says so).
Set it up in the web app¶
Lens can run Matterbridge for you, so adding Slack or WhatsApp takes no config file:
- Start the optional
matterbridgeservice once, next to Lens:
docker compose -f docker-compose.prod.yml --profile matterbridge up -d
(or add COMPOSE_PROFILES=matterbridge to Lens's .env, and run the install command again). The first start
builds Matterbridge, which takes a few minutes. It waits until Lens gives it something to do.
- Open Settings → Chat rooms, turn on Run Matterbridge here and Answer in chat rooms, and add a network:
- Slack: a bot token (
xoxb-…) and the channels it is invited to. - WhatsApp: the phone number and the group names. The page then shows a QR code: on that phone, open WhatsApp › Linked devices › Link a device and scan it. Lens stays linked after restarts. A number of its own is best.
- Telegram: a bot token from @BotFather and the chat IDs.
- Discord: a bot token, the server and its channels.
- Matrix: the homeserver, a login and password, and the rooms.
Lens writes Matterbridge's config from these settings, with a gateway for each room named after the network and the
room (slack-general, whatsapp-family-chat). The page lists the names, so you can give a room to a namespace
with Rooms for a namespace's assistant. Matterbridge restarts by itself whenever the settings change. Its API
token comes from the server's secret key, so there's nothing to copy.
The service is built with WhatsApp support, which upstream Matterbridge leaves out because a library it uses is GPL-3.0. It is a separate program that Lens only talks to over HTTP, and it runs only when you start it.
Or use a Matterbridge of your own¶
- Give Matterbridge an API account and put it in the gateway with your rooms:
[api.lens]
BindAddress="0.0.0.0:4242"
Token="a-long-random-token"
Buffer=1000
[[gateway]]
name="team"
enable=true
[[gateway.inout]]
account="slack.work"
channel="general"
[[gateway.inout]]
account="api.lens"
channel="api"
- In Lens, open Settings → Chat rooms, give the API's address (
http://matterbridge:4242on the same Docker network) and its token, and turn on Answer in chat rooms. Check the connection says whether Matterbridge answers and the account is right.
That's all. Without an account given, it answers as the admin who turned it on.
What it does¶
- Who it answers. Messages that start with its name or mention
@Lens(or every message, with Answers: every message), in every gateway or only one, from everyone in the bridged rooms or only the chat usernames listed. - As whom. It answers as one Lens account (Answers as): it reads only what that account can read. Changes it proposes (running work, changing settings) wait for approval in the web app, as in Chat; its answer says so and links to the conversation, through the Cloudflare tunnel while one is up (Settings › Remote access), so the link opens away from home too.
- Approving in the room. Reversible changes (merging, renaming or retyping entities, changing topics, new namespaces) can be approved right there: the answer says "Reply “yes” to do it", and "yes" (or "no") does it (or declines it). With several waiting, "yes all" or "yes 12". Only the chat usernames listed may message Lens and approve, so it needs that list; Approving in the room: only in the web app turns it off. Settings, imports, batch runs, extensions and forgetting are always approved in the web app. Each approval is in the audit log with "via chat room" and who said yes. An automation's action that changes something inside Lens is approved the same way, in the private chat it told you about it in, when it's the only thing waiting there (or by its number); one that reaches outside Lens or other people is approved on its card in Lens, and "no" declines either. Without a list, anyone in the room may be someone the account doesn't vouch for, so their messages count as text from outside Lens: web tools and other servers' tools wait for approval in the web app too (third-party content).
- Links it can't be made to send. Slack, Telegram, WhatsApp and most chat apps open the links in a message to
show a preview, so a web address the model built from what it read (
https://evil.example/c?d=and the notes, as a poisoned document might ask) would carry that out before anyone clicks. So a web address in an answer stays as it is only when the person, the archive or a tool wrote it, or it is Lens's own; any other is broken up (hxxps://evil[.]example/c?d=...) and an image becomes its address, still readable to copy by hand. Break up links the answer made itself (bridge.safe_links, on) turns it off. The web app needs none of this: its answers show no links or images that load by themselves. Each answer that had links broken up goes into the audit log (bridge.links_broken, with how many). - Conversations. Each person in each room is a conversation of that account's, titled
"Matterbridge · room · person", listed in Chat with the account's other conversations. Follow-ups keep their context,
and older messages are folded into a running summary (Long conversations).
Lens /clearstarts the conversation afresh andLens /compactsummarises it now. - Automations hear what's said to Lens. Each message said to Lens (and only those: the room's other talk is never
kept) is a
chat.messageactivity an automation can trigger on, "when someone messages Lens 'groceries' in Telegram" (Automations). A room given to a namespace belongs to it; any other conversation is the answering account's alone. - A namespace's own assistant. Rooms for a namespace's assistant gives a room to a namespace
(
team = pods, or one channel:team/general = pods). Conversations there are scoped to that namespace, so when its assistant is on (A namespace's own assistant) it answers with its instructions and what it remembers, and a message starting with its name is for it too. - Routing in every other room. In a room no namespace is given to, Lens is the way in to all of them and picks the namespace for each question: a message starting with a namespace assistant's name goes to that namespace (in any such room); otherwise the decision model picks the namespace the question is about, as the assistant home does (Picking the namespace). When it's sure, the answer looks there and says so once ("Looked in calls"); when it isn't, it looks everywhere and names the likeliest namespaces. use pods keeps the conversation in pods until use everything. The conversation's scope in Chat shows where the last question looked.
- Telling you things. An automation that tells you something (Automations) sends it, as the account the bot or app answers as, to the private chat in Telegram or Slack it last had with someone on its list of people who may talk to Lens. Nothing goes to a room or a group, which reaches other people too, and nothing goes through Matterbridge, where Lens can't tell a private chat from a room. Message the bot or app privately once and messages for you go there; until then an automation's run says "no chat app linked". Web addresses in them are broken up as answers' are.
- One reader. Matterbridge hands out each message once, so only one server process reads it: the API and the workers all run the bridge thread, and whichever holds the lease (renewed every look, given up when it stops or the bridge is turned off) does the reading. Settings → Chat rooms says whether it is listening and how many messages it answered.
Settings (matterbridge): run, slack_token, slack_channels, discord_token, discord_server,
discord_channels, telegram_token, telegram_chats, matrix_server, matrix_login, matrix_password,
matrix_rooms, whatsapp_number, whatsapp_groups (the tokens and the password are secrets).
Settings (bridge): enabled, url, token (a secret), account, name (default Lens), answer (mention or
all), gateway, users, approve (low_risk or off), safe_links (default on), rooms, poll_seconds (default 2). The assistant can change them too ("connect Lens to
Matterbridge at …").
Refine later: voice notes and files posted in rooms, one Lens account per chat user.