@alfe.ai/openclaw-telegram 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/bin/alfe-telegram.mjs +180 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/plugin.cjs +503 -0
- package/dist/plugin.d.cts +58 -0
- package/dist/plugin.d.ts +58 -0
- package/dist/plugin.js +503 -0
- package/dist/telegram-bridge.cjs +300 -0
- package/dist/telegram-bridge.d.cts +72 -0
- package/dist/telegram-bridge.d.ts +72 -0
- package/dist/telegram-bridge.js +289 -0
- package/openclaw.plugin.json +21 -0
- package/package.json +65 -0
- package/python/bridge.py +633 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# `@alfe.ai/openclaw-telegram`
|
|
2
|
+
|
|
3
|
+
Local Telegram user-session access for an Alfe/OpenClaw agent. It uses
|
|
4
|
+
Telethon/MTProto, so it can see the same dialog list as the signed-in Telegram
|
|
5
|
+
account instead of the restricted Bot API view.
|
|
6
|
+
|
|
7
|
+
The Telegram device session never leaves the agent machine. After enabling the
|
|
8
|
+
Telegram Connection in Alfe, run:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx -y @alfe.ai/openclaw-telegram@0.0.1 login --agent-id <agent-id>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
If the package binary is already on `PATH`, `alfe-telegram login` is the
|
|
15
|
+
equivalent shorthand. The dashboard supplies the agent-specific command; when
|
|
16
|
+
`--agent-id` is omitted, the CLI resolves it from the local agent token.
|
|
17
|
+
|
|
18
|
+
The command creates an isolated local Python runtime, asks for the API ID/hash
|
|
19
|
+
from `my.telegram.org`, then completes phone-code and optional 2FA login. It
|
|
20
|
+
does not print the resulting session.
|
|
21
|
+
|
|
22
|
+
Agent tools:
|
|
23
|
+
|
|
24
|
+
- `telegram_status`
|
|
25
|
+
- `telegram_list_chats`
|
|
26
|
+
- `telegram_list_subscriptions`
|
|
27
|
+
- `telegram_subscribe`
|
|
28
|
+
- `telegram_unsubscribe`
|
|
29
|
+
|
|
30
|
+
Subscriptions are explicit and read-only toward Telegram. Incoming text and
|
|
31
|
+
captions can trigger agent turns; generated responses are not posted back.
|
|
32
|
+
Only subscribe where you have permission to process the messages, and follow
|
|
33
|
+
Telegram's API terms.
|
|
34
|
+
|
|
35
|
+
Removing the Alfe Connection stops and uninstalls the listener but deliberately
|
|
36
|
+
does not erase a logged-in device during an ordinary reinstall. Before retiring
|
|
37
|
+
the machine, revoke the device session and remove its local state with:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx -y @alfe.ai/openclaw-telegram@0.0.1 logout --agent-id <agent-id>
|
|
41
|
+
```
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { homedir } from 'node:os'
|
|
4
|
+
import { dirname, join } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { spawnSync } from 'node:child_process'
|
|
7
|
+
import { AgentApiClient } from '@alfe.ai/agent-api-client'
|
|
8
|
+
import { resolveConfig } from '@alfe.ai/config'
|
|
9
|
+
|
|
10
|
+
const TELETHON_VERSION = '1.44.0'
|
|
11
|
+
const MAX_AGENT_ID_CHARS = 256
|
|
12
|
+
const SAFE_AGENT_ID = /^[A-Za-z0-9_-]+$/u
|
|
13
|
+
const packageDir = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
14
|
+
const bridgeScript = join(packageDir, 'python', 'bridge.py')
|
|
15
|
+
|
|
16
|
+
function privateEnv() {
|
|
17
|
+
const result = {}
|
|
18
|
+
for (const key of ['HOME', 'PATH', 'LANG', 'LC_ALL', 'TMPDIR', 'SYSTEMROOT', 'WINDIR']) {
|
|
19
|
+
if (process.env[key] !== undefined) result[key] = process.env[key]
|
|
20
|
+
}
|
|
21
|
+
result.PYTHONUNBUFFERED = '1'
|
|
22
|
+
return result
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function requireAgentId(value) {
|
|
26
|
+
if (
|
|
27
|
+
typeof value !== 'string'
|
|
28
|
+
|| value.length === 0
|
|
29
|
+
|| value.length > MAX_AGENT_ID_CHARS
|
|
30
|
+
|| !SAFE_AGENT_ID.test(value)
|
|
31
|
+
) throw new Error('Invalid agent ID')
|
|
32
|
+
return value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function resolveAgentId(explicitAgentId) {
|
|
36
|
+
if (explicitAgentId) return requireAgentId(explicitAgentId)
|
|
37
|
+
try {
|
|
38
|
+
const config = resolveConfig()
|
|
39
|
+
const identity = await new AgentApiClient({ apiKey: config.apiKey, apiUrl: config.apiUrl }).whoami()
|
|
40
|
+
return requireAgentId(identity.agentId)
|
|
41
|
+
} catch {
|
|
42
|
+
throw new Error('Could not resolve this agent identity. Pass --agent-id from the dashboard setup command.')
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function localPaths(agentId) {
|
|
47
|
+
const stateDir = join(homedir(), '.alfe', 'agents', requireAgentId(agentId), 'telegram')
|
|
48
|
+
const runtimeDir = join(stateDir, 'runtime')
|
|
49
|
+
return {
|
|
50
|
+
stateDir,
|
|
51
|
+
runtimeDir,
|
|
52
|
+
markerFile: join(runtimeDir, '.telethon-version'),
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function runtimePython(runtimeDir) {
|
|
57
|
+
const unix = join(runtimeDir, 'bin', 'python3')
|
|
58
|
+
if (existsSync(unix)) return unix
|
|
59
|
+
return join(runtimeDir, 'Scripts', 'python.exe')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function run(command, args) {
|
|
63
|
+
const result = spawnSync(command, args, {
|
|
64
|
+
stdio: 'inherit',
|
|
65
|
+
env: privateEnv(),
|
|
66
|
+
})
|
|
67
|
+
if (result.error) return 1
|
|
68
|
+
return result.status ?? 1
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function markerMatches(markerFile) {
|
|
72
|
+
try {
|
|
73
|
+
return readFileSync(markerFile, 'utf8').trim() === TELETHON_VERSION
|
|
74
|
+
} catch {
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function installRuntime(paths) {
|
|
80
|
+
mkdirSync(paths.stateDir, { recursive: true, mode: 0o700 })
|
|
81
|
+
if (!existsSync(runtimePython(paths.runtimeDir))) {
|
|
82
|
+
console.log('Creating the private Telegram Python runtime…')
|
|
83
|
+
const created = run('python3', ['-m', 'venv', paths.runtimeDir])
|
|
84
|
+
if (created !== 0) {
|
|
85
|
+
console.error('Could not create the Python runtime. Install Python 3 with venv support and retry.')
|
|
86
|
+
return created
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (markerMatches(paths.markerFile)) return 0
|
|
90
|
+
console.log(`Installing Telethon ${TELETHON_VERSION} into the private runtime…`)
|
|
91
|
+
const installed = run(runtimePython(paths.runtimeDir), [
|
|
92
|
+
'-m', 'pip', 'install',
|
|
93
|
+
'--disable-pip-version-check',
|
|
94
|
+
'--no-input',
|
|
95
|
+
`Telethon==${TELETHON_VERSION}`,
|
|
96
|
+
])
|
|
97
|
+
if (installed !== 0) {
|
|
98
|
+
console.error('Could not install Telethon. Check this machine\'s network access and retry.')
|
|
99
|
+
return installed
|
|
100
|
+
}
|
|
101
|
+
writeFileSync(paths.markerFile, `${TELETHON_VERSION}\n`, { mode: 0o600 })
|
|
102
|
+
return 0
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function printStatus(agentId, paths) {
|
|
106
|
+
const runtimeInstalled = existsSync(runtimePython(paths.runtimeDir)) && markerMatches(paths.markerFile)
|
|
107
|
+
const configured = existsSync(join(paths.stateDir, 'config.json'))
|
|
108
|
+
const sessionPresent = existsSync(join(paths.stateDir, 'user.session'))
|
|
109
|
+
console.log(`Agent: ${agentId}`)
|
|
110
|
+
console.log(`Runtime installed: ${runtimeInstalled ? 'yes' : 'no'}`)
|
|
111
|
+
console.log(`Login configured: ${configured && sessionPresent ? 'yes' : 'no'}`)
|
|
112
|
+
console.log(`Local state directory: ${paths.stateDir}`)
|
|
113
|
+
return configured && sessionPresent ? 0 : 1
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function parseAgentId(args) {
|
|
117
|
+
let agentId
|
|
118
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
119
|
+
const value = args[index]
|
|
120
|
+
if (value === '--agent-id') {
|
|
121
|
+
agentId = args[index + 1]
|
|
122
|
+
if (!agentId) throw new Error('--agent-id requires a value')
|
|
123
|
+
index += 1
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
if (value?.startsWith('--agent-id=')) {
|
|
127
|
+
agentId = value.slice('--agent-id='.length)
|
|
128
|
+
if (!agentId) throw new Error('--agent-id requires a value')
|
|
129
|
+
continue
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`Unknown option: ${value ?? ''}`)
|
|
132
|
+
}
|
|
133
|
+
return agentId
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function usage() {
|
|
137
|
+
console.log(`Usage:
|
|
138
|
+
alfe-telegram login [--agent-id ID] Install the local runtime and sign in privately
|
|
139
|
+
alfe-telegram logout [--agent-id ID] Revoke this agent's Telegram device session and remove local state
|
|
140
|
+
alfe-telegram install-runtime [--agent-id ID] Install/update this agent's pinned Telethon runtime
|
|
141
|
+
alfe-telegram status [--agent-id ID] Check this agent's local setup without revealing credentials`)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function main() {
|
|
145
|
+
const command = process.argv[2] ?? 'help'
|
|
146
|
+
if (command === 'help' || command === '--help' || command === '-h') {
|
|
147
|
+
usage()
|
|
148
|
+
return 0
|
|
149
|
+
}
|
|
150
|
+
if (!['install-runtime', 'login', 'logout', 'status'].includes(command)) {
|
|
151
|
+
usage()
|
|
152
|
+
return 2
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let agentId
|
|
156
|
+
try {
|
|
157
|
+
agentId = await resolveAgentId(parseAgentId(process.argv.slice(3)))
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error(error instanceof Error ? error.message : 'Could not resolve agent identity.')
|
|
160
|
+
return 2
|
|
161
|
+
}
|
|
162
|
+
const paths = localPaths(agentId)
|
|
163
|
+
|
|
164
|
+
if (command === 'install-runtime') return installRuntime(paths)
|
|
165
|
+
if (command === 'login') {
|
|
166
|
+
const installed = installRuntime(paths)
|
|
167
|
+
if (installed !== 0) return installed
|
|
168
|
+
return run(runtimePython(paths.runtimeDir), [bridgeScript, 'login', '--state-dir', paths.stateDir])
|
|
169
|
+
}
|
|
170
|
+
if (command === 'logout') {
|
|
171
|
+
if (!existsSync(runtimePython(paths.runtimeDir))) {
|
|
172
|
+
console.error('The local Telegram runtime is not installed for this agent.')
|
|
173
|
+
return 1
|
|
174
|
+
}
|
|
175
|
+
return run(runtimePython(paths.runtimeDir), [bridgeScript, 'logout', '--state-dir', paths.stateDir])
|
|
176
|
+
}
|
|
177
|
+
return printStatus(agentId, paths)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
process.exitCode = await main()
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_telegram_bridge = require("./telegram-bridge.cjs");
|
|
3
|
+
exports.TelegramBridge = require_telegram_bridge.TelegramBridge;
|
|
4
|
+
exports.TelegramBridgeUnavailable = require_telegram_bridge.TelegramBridgeUnavailable;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as TelegramBridgeUnavailable, i as TelegramBridgeStatus, n as TelegramBridgeEvent, r as TelegramBridgeLogger, t as TelegramBridge } from "./telegram-bridge.cjs";
|
|
2
|
+
export { TelegramBridge, type TelegramBridgeEvent, type TelegramBridgeLogger, type TelegramBridgeStatus, TelegramBridgeUnavailable };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as TelegramBridgeUnavailable, i as TelegramBridgeStatus, n as TelegramBridgeEvent, r as TelegramBridgeLogger, t as TelegramBridge } from "./telegram-bridge.js";
|
|
2
|
+
export { TelegramBridge, type TelegramBridgeEvent, type TelegramBridgeLogger, type TelegramBridgeStatus, TelegramBridgeUnavailable };
|
package/dist/index.js
ADDED