Skip to content

Commit 0d5b8b4

Browse files
clharmanclaude
andcommitted
v0.3.0
Features: - Add image/GIF upload support for Telegram - Add loading spinner when starting `afk-code claude` - Show project directory in Telegram session list (thanks @AnveshJarabani) Fixes: - Add `files:write` scope to Slack manifest for image uploads - Add "Attach Files" permission to Discord setup instructions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent bfae631 commit 0d5b8b4

6 files changed

Lines changed: 55 additions & 4 deletions

File tree

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Telegram and Discord are recommended.
1313
| Siri integration | Receive & Send | Receive only | Receive only |
1414
| Multi-session support | One at a time (switchable) | Yes | Yes |
1515
| Permissions required | Personal | Personal | Admin |
16+
| Image support | Yes | Yes | Yes |
1617

1718
## Quick Start (Telegram)
1819

@@ -41,7 +42,7 @@ npx afk-code claude
4142
# - Go to Bot → Reset Token → copy it
4243
# - Enable "Message Content Intent"
4344
# - Go to OAuth2 → URL Generator → select "bot" scope
44-
# - Select permissions: Send Messages, Manage Channels, Read Message History
45+
# - Select permissions: Send Messages, Manage Channels, Read Message History, Attach Files
4546
# - Open the generated URL to invite the bot
4647

4748
# 2. Get your User ID (enable Developer Mode, right-click your name → Copy User ID)
@@ -75,6 +76,10 @@ npx afk-code claude
7576

7677
A new channel is created for each session. Messages relay bidirectionally.
7778

79+
## Image Support
80+
81+
When Claude references image paths in responses (e.g., `/path/to/screenshot.png`), the bot automatically detects and uploads them to the chat. Supports PNG, JPG, GIF, WebP, and other common formats.
82+
7883
## Commands
7984

8085
```
@@ -128,7 +133,8 @@ Requires Node.js 18+.
128133
## Limitations
129134

130135
- Does not support plan mode or responding to Claude Code's form-based questions (AskUserQuestion)
131-
- Does not send tool calls or results
136+
- You can bypass this using the `/mode` command or by sending any message
137+
- Does not send tool calls or results (would encounter rate limits)
132138

133139
## Disclaimer
134140

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "afk-code",
3-
"version": "0.2.1",
3+
"version": "0.3.0",
44
"description": "Monitor and interact with Claude Code sessions from Slack/Discord/Telegram",
55
"author": "Colin Harman",
66
"repository": {

slack-manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"chat:write",
5757
"chat:write.customize",
5858
"commands",
59+
"files:write",
5960
"groups:history",
6061
"groups:write",
6162
"users:read"

src/cli/discord.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Step 3: Invite the Bot
5959
• Send Messages
6060
• Manage Channels
6161
• Read Message History
62+
• Attach Files
6263
4. Copy the URL and open it to invite the bot to your server
6364
`);
6465

src/cli/run.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,23 @@ export async function run(command: string[]): Promise<void> {
7272
const cwd = process.cwd();
7373
const projectDir = getClaudeProjectDir(cwd);
7474

75+
// Show loading spinner while starting
76+
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
77+
let spinnerIndex = 0;
78+
let spinnerInterval: ReturnType<typeof setInterval> | null = setInterval(() => {
79+
process.stdout.write(`\r${spinnerFrames[spinnerIndex]} Starting...`);
80+
spinnerIndex = (spinnerIndex + 1) % spinnerFrames.length;
81+
}, 80);
82+
83+
const stopSpinner = () => {
84+
if (spinnerInterval) {
85+
clearInterval(spinnerInterval);
86+
spinnerInterval = null;
87+
// Clear the spinner line
88+
process.stdout.write('\r\x1b[K');
89+
}
90+
};
91+
7592
// Use node-pty for full terminal features + remote input
7693
const cols = process.stdout.columns || 80;
7794
const rows = process.stdout.rows || 24;
@@ -99,6 +116,7 @@ export async function run(command: string[]): Promise<void> {
99116
}
100117

101118
ptyProcess.onData((data: string) => {
119+
stopSpinner();
102120
process.stdout.write(data);
103121
});
104122

src/telegram/telegram-app.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { Bot, Context } from 'grammy';
1+
import { Bot, Context, InputFile } from 'grammy';
22
import type { TelegramConfig } from './types.js';
33
import { SessionManager, type SessionInfo } from '../slack/session-manager.js';
44
import { chunkMessage, formatTodos } from '../slack/message-formatter.js';
5+
import { extractImagePaths } from '../utils/image-extractor.js';
56

67
// Telegram has a 4096 character limit per message
78
const MAX_MESSAGE_LENGTH = 4000;
@@ -144,6 +145,30 @@ export function createTelegramApp(config: TelegramConfig) {
144145
await sendChunkedMessage(content, `_User (terminal):_`, { disable_notification: true });
145146
} else {
146147
await sendChunkedMessage(content, `_Claude Code:_`);
148+
149+
// Extract and upload any images mentioned in the response
150+
const session = sessionManager.getSession(sessionId);
151+
const images = extractImagePaths(content, session?.cwd);
152+
for (const image of images) {
153+
try {
154+
console.log(`[Telegram] Uploading image: ${image.resolvedPath}`);
155+
const isGif = image.resolvedPath.toLowerCase().endsWith('.gif');
156+
messageQueue.push(async () => {
157+
if (isGif) {
158+
await bot.api.sendAnimation(config.chatId, new InputFile(image.resolvedPath), {
159+
caption: `📎 ${image.originalPath}`,
160+
});
161+
} else {
162+
await bot.api.sendPhoto(config.chatId, new InputFile(image.resolvedPath), {
163+
caption: `📎 ${image.originalPath}`,
164+
});
165+
}
166+
});
167+
processQueue();
168+
} catch (err) {
169+
console.error('[Telegram] Failed to upload image:', err);
170+
}
171+
}
147172
}
148173
},
149174

0 commit comments

Comments
 (0)