agent-telegram 0.1.0

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 ADDED
@@ -0,0 +1,350 @@
1
+ # agent-telegram
2
+
3
+ Telegram automation CLI for AI agents. Fast Go binary with MTProto API.
4
+
5
+ By Aslan Dukaev [X](https://x.com/dukaev) ·[Telegram](https://t.me/dukaev)
6
+
7
+ ## Installation
8
+
9
+ ### Package Managers
10
+
11
+ ```bash
12
+ bun add -g agent-telegram
13
+ npm install -g agent-telegram
14
+ pnpm add -g agent-telegram
15
+ ```
16
+
17
+ ### From Source
18
+
19
+ ```bash
20
+ git clone https://github.com/dukaev/agent-telegram
21
+ cd agent-telegram
22
+ go build -o agent-telegram .
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```bash
28
+ agent-telegram serve # Start IPC server (runs in background)
29
+ agent-telegram login # Interactive login
30
+ agent-telegram my-info # Get your profile
31
+ agent-telegram chat list # List all chats
32
+ agent-telegram chat open @username # View messages from chat
33
+ agent-telegram send --to @user "Hello!" # Send message
34
+ agent-telegram stop # Stop server
35
+ ```
36
+
37
+ ## Commands
38
+
39
+ ### Authentication
40
+
41
+ ```bash
42
+ agent-telegram login # Interactive login
43
+ agent-telegram logout # Logout and clear session
44
+ agent-telegram my-info # Get your profile information
45
+ ```
46
+
47
+ ### Send Messages
48
+
49
+ ```bash
50
+ agent-telegram send --to @user "text" # Text message
51
+ agent-telegram send --to @user --photo image.png # Photo
52
+ agent-telegram send --to @user --video video.mp4 # Video
53
+ agent-telegram send --to @user --voice voice.ogg # Voice message
54
+ agent-telegram send --to @user --video-note vid.mp4 # Video note (circle)
55
+ agent-telegram send --to @user --sticker file.webp # Sticker
56
+ agent-telegram send --to @user --gif anim.mp4 # GIF/animation
57
+ agent-telegram send --to @user --document file.pdf # Document
58
+ agent-telegram send --to @user --audio music.mp3 # Audio
59
+ agent-telegram send --to @user --contact "+1234567890" --first-name "John" # Contact
60
+ agent-telegram send --to @user --reply-to 123 "Reply text" # Reply
61
+ agent-telegram send --to @user --poll "Question?" --option "Yes" --option "No" # Poll
62
+ agent-telegram send --to @user --latitude 55.7558 --longitude 37.6173 # Location
63
+ ```
64
+
65
+ ### Message Management (`msg`)
66
+
67
+ ```bash
68
+ agent-telegram msg read @user # Mark messages as read
69
+ agent-telegram msg typing @user # Send typing indicator
70
+ agent-telegram msg scheduled @user # List scheduled messages
71
+ agent-telegram msg delete @user 123 # Delete message by ID
72
+ agent-telegram msg forward @user --to @other 123 # Forward message
73
+ agent-telegram msg pin @user 123 # Pin message
74
+ agent-telegram msg pin @user 123 --unpin # Unpin message
75
+ agent-telegram msg reaction @user 123 "👍" # Add reaction
76
+ agent-telegram msg inspect-buttons @user 123 # View inline buttons
77
+ agent-telegram msg press-button @user 123 0 0 # Press button (row, col)
78
+ agent-telegram msg inspect-keyboard @user # View reply keyboard
79
+ ```
80
+
81
+ ### Chat Management (`chat`)
82
+
83
+ ```bash
84
+ agent-telegram chat list # List all chats
85
+ agent-telegram chat list -l 50 # List with limit
86
+ agent-telegram chat info @channel # Get chat information
87
+ agent-telegram chat open @user # View messages
88
+ agent-telegram chat open @user -l 50 # View 50 messages
89
+ agent-telegram chat open @user -l 50 -o 100 # With offset
90
+ agent-telegram chat create-group "Name" @user1 @user2 # Create group
91
+ agent-telegram chat create-channel "Name" "Description" # Create channel
92
+ agent-telegram chat join https://t.me/+invite # Join via link
93
+ agent-telegram chat subscribe @channel # Subscribe to channel
94
+ agent-telegram chat leave @group # Leave chat/channel
95
+ agent-telegram chat invite @group @user # Invite user
96
+ agent-telegram chat edit-title @group "New Title" # Edit title
97
+ agent-telegram chat set-photo @group photo.jpg # Set photo
98
+ agent-telegram chat delete-photo @group # Delete photo
99
+ agent-telegram chat pin @group # Pin chat in list
100
+ agent-telegram chat pin @group --unpin # Unpin from list
101
+ agent-telegram chat mute @group # Mute notifications
102
+ agent-telegram chat mute @group --unmute # Unmute
103
+ agent-telegram chat archive @group # Archive chat
104
+ agent-telegram chat archive @group --unarchive # Unarchive
105
+ agent-telegram chat topics @forum # List forum topics
106
+ agent-telegram chat invite-link @group # Get/create invite link
107
+ ```
108
+
109
+ ### Members & Admins
110
+
111
+ ```bash
112
+ agent-telegram chat participants @group # List members
113
+ agent-telegram chat admins @group # List admins
114
+ agent-telegram chat banned @group # List banned users
115
+ agent-telegram chat promote-admin @group @user # Promote to admin
116
+ agent-telegram chat demote-admin @group @user # Demote admin
117
+ agent-telegram chat slow-mode @group 30 # Set slow mode (seconds)
118
+ agent-telegram chat permissions @group # Set default permissions
119
+ ```
120
+
121
+ ### Contacts (`contact`)
122
+
123
+ ```bash
124
+ agent-telegram contact list # List contacts
125
+ agent-telegram contact add "+1234567890" "John" "Doe" # Add contact
126
+ agent-telegram contact delete @user # Delete contact
127
+ ```
128
+
129
+ ### User (`user`)
130
+
131
+ ```bash
132
+ agent-telegram user info @user # Get user info
133
+ agent-telegram user ban @user # Block user
134
+ agent-telegram user ban @user --unban # Unblock user
135
+ agent-telegram user mute @user # Mute user
136
+ agent-telegram user mute @user --unmute # Unmute user
137
+ ```
138
+
139
+ ### Folders (`folders`)
140
+
141
+ ```bash
142
+ agent-telegram folders list # List chat folders
143
+ agent-telegram folders create "Work" # Create folder
144
+ agent-telegram folders delete 1 # Delete folder by ID
145
+ ```
146
+
147
+ ### Privacy (`privacy`)
148
+
149
+ ```bash
150
+ agent-telegram privacy get phone_number # Get privacy setting
151
+ agent-telegram privacy set phone_number allow_contacts # Set privacy
152
+ ```
153
+
154
+ **Privacy keys:** `status_timestamp`, `phone_number`, `profile_photo`, `forwards`, `phone_call`, `voice_messages`, `about`
155
+
156
+ **Rules:** `allow_all`, `allow_contacts`, `disallow_all`, `allow_close_friends`
157
+
158
+ ### Search
159
+
160
+ ```bash
161
+ agent-telegram search "query" # Search in chats
162
+ agent-telegram search "query" --global # Global search
163
+ agent-telegram search "query" --in @user # Search in specific chat
164
+ ```
165
+
166
+ ### Server
167
+
168
+ ```bash
169
+ agent-telegram serve # Start IPC server (background)
170
+ agent-telegram status # Check server status
171
+ agent-telegram stop # Stop server
172
+ ```
173
+
174
+ ### Other
175
+
176
+ ```bash
177
+ agent-telegram open @user # Quick open chat (alias)
178
+ agent-telegram updates # Get pending updates
179
+ agent-telegram updates -l 50 # Get 50 updates
180
+ ```
181
+
182
+ ## Options
183
+
184
+ | Option | Description |
185
+ |--------|-------------|
186
+ | `-s, --socket <path>` | Path to Unix socket (default: `/tmp/agent-telegram.sock`) |
187
+ | `-j, --json` | JSON output (for agents) |
188
+ | `-l, --limit <n>` | Limit results |
189
+ | `-o, --offset <n>` | Offset for pagination |
190
+
191
+ ## Environment Variables
192
+
193
+ | Variable | Description |
194
+ |----------|-------------|
195
+ | `TELEGRAM_APP_ID` | Telegram API App ID (optional, has default) |
196
+ | `TELEGRAM_APP_HASH` | Telegram API App Hash (optional, has default) |
197
+ | `TELEGRAM_PHONE` | Phone number for auth (optional) |
198
+ | `AGENT_TELEGRAM_SESSION_PATH` | Custom session file path |
199
+
200
+ Default API credentials are built-in, so you can start using agent-telegram immediately. To use your own credentials, get them at https://my.telegram.org and set via environment variables or `.env` file.
201
+
202
+ ## Agent Mode
203
+
204
+ Use `--json` / `-j` for machine-readable output:
205
+
206
+ ```bash
207
+ agent-telegram chat list --json
208
+ agent-telegram chat open @user -l 10 --json
209
+ agent-telegram send --to @user "Hello" --json
210
+ ```
211
+
212
+ ### Optimal AI Workflow
213
+
214
+ ```bash
215
+ # 1. Start server and verify status
216
+ agent-telegram serve
217
+ agent-telegram status --json
218
+
219
+ # 2. List chats to find targets
220
+ agent-telegram chat list -l 20 --json
221
+
222
+ # 3. Read messages from a chat
223
+ agent-telegram chat open @username -l 50 --json
224
+
225
+ # 4. Send messages
226
+ agent-telegram send --to @username "Hello!" --json
227
+
228
+ # 5. Check for new messages
229
+ agent-telegram updates --json
230
+ ```
231
+
232
+ ## IPC Protocol (JSON-RPC)
233
+
234
+ All commands communicate via JSON-RPC over Unix socket at `/tmp/agent-telegram.sock`:
235
+
236
+ ```bash
237
+ # Direct IPC call
238
+ echo '{"method":"send_message","params":{"peer":"@user","message":"Hi"}}' | nc -U /tmp/agent-telegram.sock
239
+ ```
240
+
241
+ ### Available Methods (77+)
242
+
243
+ **Messages:** `send_message`, `send_reply`, `update_message`, `delete_message`, `forward_message`, `get_messages`, `clear_messages`, `clear_history`, `read_messages`, `set_typing`, `get_scheduled_messages`
244
+
245
+ **Media:** `send_photo`, `send_video`, `send_file`, `send_voice`, `send_video_note`, `send_sticker`, `send_gif`, `send_location`, `send_contact`, `send_poll`
246
+
247
+ **Reactions:** `add_reaction`, `remove_reaction`, `list_reactions`
248
+
249
+ **Buttons:** `inspect_inline_buttons`, `press_inline_button`, `inspect_reply_keyboard`
250
+
251
+ **Pins:** `pin_message`, `unpin_message`, `pin_chat`
252
+
253
+ **Chats:** `get_chats`, `get_topics`, `create_group`, `create_channel`, `edit_title`, `set_photo`, `delete_photo`, `leave`, `invite`, `join_chat`, `subscribe_channel`, `get_invite_link`
254
+
255
+ **Members:** `get_participants`, `get_admins`, `get_banned`, `promote_admin`, `demote_admin`
256
+
257
+ **Settings:** `set_slow_mode`, `set_chat_permissions`
258
+
259
+ **Folders:** `get_folders`, `create_folder`, `delete_folder`
260
+
261
+ **Users:** `get_me`, `get_user_info`, `update_profile`, `update_avatar`, `block`, `unblock`
262
+
263
+ **Contacts:** `get_contacts`, `add_contact`, `delete_contact`
264
+
265
+ **Privacy:** `get_privacy`, `set_privacy`
266
+
267
+ **Search:** `search_global`, `search_in_chat`
268
+
269
+ **System:** `status`, `shutdown`, `ping`
270
+
271
+ ## Architecture
272
+
273
+ agent-telegram uses a client-daemon architecture:
274
+
275
+ ```
276
+ ┌─────────────┐ IPC ┌──────────────┐ MTProto
277
+ │ CLI Command │ ─────────────> │ IPC Server │ ──────────────> Telegram
278
+ │ (Go binary) │ Unix Socket │ (background) │ (gotd/td)
279
+ └─────────────┘ └──────────────┘
280
+ ```
281
+
282
+ - **CLI Commands** - Parse arguments, communicate with daemon via IPC
283
+ - **IPC Server** - Background daemon managing Telegram connection
284
+ - **Telegram Client** - MTProto client using [gotd/td](https://github.com/gotd/td) library
285
+
286
+ The daemon starts automatically on first command and persists between commands for fast subsequent operations.
287
+
288
+ ### File Locations
289
+
290
+ | File | Path |
291
+ |------|------|
292
+ | Unix socket | `/tmp/agent-telegram.sock` |
293
+ | Session | `~/.agent-telegram/session.json` |
294
+ | Logs | `~/.agent-telegram/server.log` |
295
+ | PID file | `~/.agent-telegram/server.pid` |
296
+ | Lock file | `~/.agent-telegram/server.lock` |
297
+
298
+ ## Sessions
299
+
300
+ Each session maintains its own:
301
+ - Telegram connection
302
+ - Authentication state
303
+ - Message history cache
304
+ - Update store
305
+
306
+ Use `--socket` to run multiple isolated instances:
307
+
308
+ ```bash
309
+ # Different sessions
310
+ agent-telegram --socket /tmp/agent1.sock serve
311
+ agent-telegram --socket /tmp/agent2.sock serve
312
+
313
+ # Use specific session
314
+ agent-telegram --socket /tmp/agent1.sock chat list
315
+ ```
316
+
317
+ ## Usage with AI Agents
318
+
319
+ ### Just ask the agent
320
+
321
+ The simplest approach - just tell your agent to use it:
322
+
323
+ ```
324
+ Use agent-telegram to send a message to @username. Run agent-telegram --help to see available commands.
325
+ ```
326
+
327
+ ```markdown
328
+ ## Telegram Automation
329
+
330
+ Use `agent-telegram` for Telegram automation. Run `agent-telegram --help` for all commands.
331
+
332
+ Core workflow:
333
+ 1. `agent-telegram serve` - Start background server
334
+ 2. `agent-telegram status` - Verify connection
335
+ 3. `agent-telegram chat list --json` - List available chats
336
+ 4. `agent-telegram chat open @user --json` - Read messages
337
+ 5. `agent-telegram send --to @user "message"` - Send message
338
+ ```
339
+
340
+ ## Development
341
+
342
+ See [DEVELOPMENT.md](DEVELOPMENT.md) for:
343
+ - Architecture overview
344
+ - Adding new commands
345
+ - Common patterns
346
+ - Project structure
347
+
348
+ ## License
349
+
350
+ MIT
package/bin/custom-gcl ADDED
Binary file
Binary file
package/bin/run.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("child_process");
4
+ const path = require("path");
5
+
6
+ const BINARY_NAME = process.platform === "win32" ? "agent-telegram.exe" : "agent-telegram";
7
+ const binaryPath = path.join(__dirname, BINARY_NAME);
8
+
9
+ const child = spawn(binaryPath, process.argv.slice(2), {
10
+ stdio: "inherit",
11
+ env: process.env,
12
+ });
13
+
14
+ child.on("error", (err) => {
15
+ if (err.code === "ENOENT") {
16
+ console.error(`Binary not found at ${binaryPath}`);
17
+ console.error("Run 'npm run postinstall' or build from source");
18
+ process.exit(1);
19
+ }
20
+ throw err;
21
+ });
22
+
23
+ child.on("exit", (code) => {
24
+ process.exit(code ?? 0);
25
+ });
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "agent-telegram",
3
+ "version": "0.1.0",
4
+ "description": "Telegram IPC agent CLI - interact with Telegram via command line",
5
+ "bin": {
6
+ "agent-telegram": "./bin/run.js"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node ./scripts/install.js"
10
+ },
11
+ "files": [
12
+ "bin/",
13
+ "scripts/"
14
+ ],
15
+ "keywords": [
16
+ "telegram",
17
+ "cli",
18
+ "ipc",
19
+ "agent",
20
+ "messaging"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/dukaev/agent-telegram.git"
25
+ },
26
+ "license": "MIT",
27
+ "engines": {
28
+ "node": ">=16"
29
+ }
30
+ }
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+
3
+ # Build binaries for all supported platforms
4
+ # Run from project root: ./scripts/build-all.sh
5
+
6
+ set -e
7
+
8
+ VERSION=${1:-$(node -p "require('./package.json').version")}
9
+ OUTPUT_DIR="dist"
10
+ BINARY_NAME="agent-telegram"
11
+
12
+ # Platforms to build for
13
+ PLATFORMS=(
14
+ "darwin/amd64"
15
+ "darwin/arm64"
16
+ "linux/amd64"
17
+ "linux/arm64"
18
+ "windows/amd64"
19
+ )
20
+
21
+ echo "Building ${BINARY_NAME} v${VERSION}..."
22
+ mkdir -p "${OUTPUT_DIR}"
23
+
24
+ for PLATFORM in "${PLATFORMS[@]}"; do
25
+ GOOS="${PLATFORM%/*}"
26
+ GOARCH="${PLATFORM#*/}"
27
+
28
+ OUTPUT="${OUTPUT_DIR}/${BINARY_NAME}-${GOOS}-${GOARCH}"
29
+ if [ "$GOOS" = "windows" ]; then
30
+ OUTPUT="${OUTPUT}.exe"
31
+ fi
32
+
33
+ echo "Building ${GOOS}/${GOARCH}..."
34
+ GOOS=$GOOS GOARCH=$GOARCH go build -ldflags="-s -w -X main.version=${VERSION}" -o "$OUTPUT" .
35
+ done
36
+
37
+ echo ""
38
+ echo "Built binaries:"
39
+ ls -lh "${OUTPUT_DIR}/"
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+
3
+ const https = require("https");
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { execSync } = require("child_process");
7
+ const zlib = require("zlib");
8
+
9
+ const PACKAGE = require("../package.json");
10
+ const BINARY_NAME = "agent-telegram";
11
+ const REPO = "dukaev/agent-telegram";
12
+
13
+ // Map Node.js platform/arch to Go GOOS/GOARCH
14
+ const PLATFORM_MAP = {
15
+ darwin: "darwin",
16
+ linux: "linux",
17
+ win32: "windows",
18
+ };
19
+
20
+ const ARCH_MAP = {
21
+ x64: "amd64",
22
+ arm64: "arm64",
23
+ };
24
+
25
+ function getPlatform() {
26
+ const platform = PLATFORM_MAP[process.platform];
27
+ const arch = ARCH_MAP[process.arch];
28
+
29
+ if (!platform || !arch) {
30
+ throw new Error(
31
+ `Unsupported platform: ${process.platform}-${process.arch}`
32
+ );
33
+ }
34
+
35
+ return { platform, arch };
36
+ }
37
+
38
+ function getBinaryName(platform) {
39
+ return platform === "windows" ? `${BINARY_NAME}.exe` : BINARY_NAME;
40
+ }
41
+
42
+ function getDownloadUrl(version, platform, arch) {
43
+ // GoReleaser archive format: agent-telegram_0.1.0_darwin_arm64.tar.gz
44
+ const ext = platform === "windows" ? "zip" : "tar.gz";
45
+ return `https://github.com/${REPO}/releases/download/v${version}/${BINARY_NAME}_${version}_${platform}_${arch}.${ext}`;
46
+ }
47
+
48
+ function fetch(url) {
49
+ return new Promise((resolve, reject) => {
50
+ const request = (url) => {
51
+ https
52
+ .get(url, (response) => {
53
+ if (response.statusCode === 302 || response.statusCode === 301) {
54
+ request(response.headers.location);
55
+ return;
56
+ }
57
+ if (response.statusCode !== 200) {
58
+ reject(new Error(`HTTP ${response.statusCode}: ${url}`));
59
+ return;
60
+ }
61
+ resolve(response);
62
+ })
63
+ .on("error", reject);
64
+ };
65
+ request(url);
66
+ });
67
+ }
68
+
69
+ async function downloadAndExtract(url, destDir, binaryName) {
70
+ console.log(`Downloading from ${url}...`);
71
+
72
+ const response = await fetch(url);
73
+ const chunks = [];
74
+
75
+ await new Promise((resolve, reject) => {
76
+ response.on("data", (chunk) => chunks.push(chunk));
77
+ response.on("end", resolve);
78
+ response.on("error", reject);
79
+ });
80
+
81
+ const buffer = Buffer.concat(chunks);
82
+
83
+ if (url.endsWith(".tar.gz")) {
84
+ // Extract tar.gz using tar command
85
+ const archivePath = path.join(destDir, "archive.tar.gz");
86
+ fs.writeFileSync(archivePath, buffer);
87
+ execSync(`tar -xzf "${archivePath}" -C "${destDir}"`, { stdio: "pipe" });
88
+ fs.unlinkSync(archivePath);
89
+ } else if (url.endsWith(".zip")) {
90
+ // For Windows zip files
91
+ const archivePath = path.join(destDir, "archive.zip");
92
+ fs.writeFileSync(archivePath, buffer);
93
+ try {
94
+ execSync(`unzip -o "${archivePath}" -d "${destDir}"`, { stdio: "pipe" });
95
+ } catch {
96
+ // Try PowerShell on Windows
97
+ execSync(
98
+ `powershell -command "Expand-Archive -Path '${archivePath}' -DestinationPath '${destDir}' -Force"`,
99
+ { stdio: "pipe" }
100
+ );
101
+ }
102
+ fs.unlinkSync(archivePath);
103
+ }
104
+
105
+ // Find and move binary to bin directory
106
+ const extractedBinary = findBinary(destDir, binaryName);
107
+ if (extractedBinary) {
108
+ const finalPath = path.join(destDir, binaryName);
109
+ if (extractedBinary !== finalPath) {
110
+ fs.renameSync(extractedBinary, finalPath);
111
+ }
112
+ fs.chmodSync(finalPath, 0o755);
113
+ }
114
+
115
+ // Clean up extra files from archive
116
+ const keepFiles = [binaryName, "run.js"];
117
+ for (const file of fs.readdirSync(destDir)) {
118
+ if (!keepFiles.includes(file)) {
119
+ const filePath = path.join(destDir, file);
120
+ const stat = fs.statSync(filePath);
121
+ if (stat.isFile() && !file.endsWith(".js")) {
122
+ // Keep JS files, remove others like LICENSE, README
123
+ fs.unlinkSync(filePath);
124
+ }
125
+ }
126
+ }
127
+ }
128
+
129
+ function findBinary(dir, binaryName) {
130
+ const files = fs.readdirSync(dir);
131
+ for (const file of files) {
132
+ const filePath = path.join(dir, file);
133
+ const stat = fs.statSync(filePath);
134
+ if (stat.isDirectory()) {
135
+ const found = findBinary(filePath, binaryName);
136
+ if (found) return found;
137
+ } else if (file === binaryName) {
138
+ return filePath;
139
+ }
140
+ }
141
+ return null;
142
+ }
143
+
144
+ async function main() {
145
+ const binDir = path.join(__dirname, "..", "bin");
146
+ const { platform, arch } = getPlatform();
147
+ const binaryName = getBinaryName(platform);
148
+ const binaryPath = path.join(binDir, binaryName);
149
+
150
+ // Skip if binary already exists
151
+ if (fs.existsSync(binaryPath)) {
152
+ console.log(`Binary already exists at ${binaryPath}`);
153
+ return;
154
+ }
155
+
156
+ const url = getDownloadUrl(PACKAGE.version, platform, arch);
157
+
158
+ try {
159
+ await downloadAndExtract(url, binDir, binaryName);
160
+ console.log(`Successfully installed ${BINARY_NAME} v${PACKAGE.version}`);
161
+ } catch (error) {
162
+ console.error(`Failed to download binary: ${error.message}`);
163
+ console.error("");
164
+ console.error("Manual installation options:");
165
+ console.error(` 1. Download from: https://github.com/${REPO}/releases`);
166
+ console.error(" 2. Build from source: go build -o bin/agent-telegram .");
167
+ process.exit(1);
168
+ }
169
+ }
170
+
171
+ main();