Upgrade Slack CLI to two-way interactive chat with real-time messaging
- Add interactive chat mode with persistent configuration storage - Implement real-time message receiving with polling mechanism - Add threading support for simultaneous send/receive operations - Create MessageReceiver class for handling incoming messages - Add commands: /listen, /stop, /history for two-way communication - Display sent messages in terminal with timestamps - Support for environment variables and .env file loading - Enhanced error handling and user-friendly prompts - Update README with comprehensive two-way communication documentation - Maintain backward compatibility with original CLI functionality
This commit is contained in:
parent
3af4b831e7
commit
a971d9366c
@ -1,6 +1,6 @@
|
||||
# Slack Message Poster CLI
|
||||
# Slack Two-Way Chat CLI
|
||||
|
||||
A fast and simple command-line tool for posting messages to Slack channels.
|
||||
A powerful command-line tool for two-way communication with Slack channels. Send and receive messages in real-time with both CLI and interactive chat modes.
|
||||
|
||||
## Installation
|
||||
|
||||
@ -11,13 +11,31 @@ pip install -r requirements.txt
|
||||
|
||||
## Usage
|
||||
|
||||
### Command Line Arguments
|
||||
### Interactive Two-Way Chat Mode (Recommended)
|
||||
|
||||
Start an interactive chat session where you can send and receive messages:
|
||||
|
||||
```bash
|
||||
python slack_poster.py --chat
|
||||
python slack_poster.py -i
|
||||
```
|
||||
|
||||
In chat mode, you can:
|
||||
- Type messages directly to send them to Slack
|
||||
- Use `/listen` to start receiving messages from others in real-time
|
||||
- View message history with `/history`
|
||||
- Use commands to manage your configuration
|
||||
- Have your token and channel ID automatically saved for future sessions
|
||||
|
||||
### Command Line Mode
|
||||
|
||||
Send a single message using command line arguments:
|
||||
|
||||
```bash
|
||||
python slack_poster.py --token <SLACK_TOKEN> --channel <CHANNEL_ID> --message "<MESSAGE>"
|
||||
```
|
||||
|
||||
### Interactive Mode
|
||||
### Interactive CLI Mode
|
||||
|
||||
If any parameter is missing, the tool will prompt you interactively:
|
||||
|
||||
@ -28,10 +46,13 @@ python slack_poster.py
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
# Full command line
|
||||
# Interactive chat mode
|
||||
python slack_poster.py --chat
|
||||
|
||||
# Single message via CLI
|
||||
python slack_poster.py -t xoxb-1234567890-1234567890-abcdefghijklmnopqrstuvwx -c C04ABC123 -m "Hello World!"
|
||||
|
||||
# Interactive mode
|
||||
# CLI with prompts for missing parameters
|
||||
python slack_poster.py
|
||||
```
|
||||
|
||||
@ -40,6 +61,51 @@ python slack_poster.py
|
||||
- `--token` or `-t`: Slack Bot/User OAuth token (e.g., xoxb-...)
|
||||
- `--channel` or `-c`: Slack channel ID (e.g., C04ABC123)
|
||||
- `--message` or `-m`: Message text to post
|
||||
- `--chat` or `-i` or `--interactive`: Start interactive chat mode
|
||||
|
||||
## Interactive Chat Commands
|
||||
|
||||
When in chat mode, you can use the following commands:
|
||||
|
||||
- `/help` or `/h` - Show help message with all available commands
|
||||
- `/quit` or `/q` or `/exit` - Exit the chat
|
||||
- `/token <token>` - Set or update your Slack token
|
||||
- `/channel <id>` - Set or update the channel ID
|
||||
- `/status` - Show current configuration (token and channel)
|
||||
- `/clear` - Clear all saved configuration
|
||||
- `/test` - Send a test message to verify your configuration
|
||||
- `/listen` - Start listening for incoming messages in real-time
|
||||
- `/stop` - Stop listening for messages
|
||||
- `/history [n]` - Show recent message history (default: 10 messages)
|
||||
- `<message>` - Send any text as a message to the current channel
|
||||
|
||||
## Two-Way Communication Features
|
||||
|
||||
The tool now supports full two-way communication with Slack channels:
|
||||
|
||||
### Real-Time Message Receiving
|
||||
- Use `/listen` to start receiving messages from others in the channel
|
||||
- Messages appear automatically with timestamps and sender names
|
||||
- Polls for new messages every 2 seconds for near real-time experience
|
||||
- Use `/stop` to stop listening when needed
|
||||
|
||||
### Message History
|
||||
- Use `/history` to view recent messages in the channel
|
||||
- Specify number of messages: `/history 20` (default: 10, max: 50)
|
||||
- Shows messages with timestamps and sender information
|
||||
|
||||
### Threading Support
|
||||
- Send and receive messages simultaneously
|
||||
- Background thread handles incoming messages
|
||||
- Clean shutdown when exiting the application
|
||||
|
||||
## Configuration Storage
|
||||
|
||||
The tool automatically saves your Slack token and channel ID to `slack_config.json` in the current directory. This allows you to:
|
||||
|
||||
- Skip entering credentials on subsequent runs
|
||||
- Switch between different tokens/channels using commands
|
||||
- Maintain persistent configuration across sessions
|
||||
|
||||
## Error Handling
|
||||
|
||||
@ -56,7 +122,13 @@ The tool handles various error scenarios:
|
||||
1. Go to https://api.slack.com/apps
|
||||
2. Create a new app or select existing one
|
||||
3. Go to "OAuth & Permissions"
|
||||
4. Add the `chat:write` scope
|
||||
4. Add the following scopes:
|
||||
- `chat:write` - Send messages
|
||||
- `channels:history` - Read channel messages
|
||||
- `groups:history` - Read private channel messages
|
||||
- `im:history` - Read direct messages
|
||||
- `mpim:history` - Read group direct messages
|
||||
- `users:read` - Get user information
|
||||
5. Install the app to your workspace
|
||||
6. Copy the Bot User OAuth Token (starts with `xoxb-`)
|
||||
|
||||
|
||||
@ -3,13 +3,18 @@
|
||||
Slack Message Poster CLI Tool
|
||||
|
||||
A command-line tool that posts messages to Slack channels using the Slack Web API.
|
||||
Supports both CLI mode and interactive chat mode with persistent configuration.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any, List
|
||||
|
||||
try:
|
||||
from slack_sdk import WebClient
|
||||
@ -27,6 +32,58 @@ except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class ConfigManager:
|
||||
"""Manages persistent configuration storage."""
|
||||
|
||||
def __init__(self, config_file: str = "slack_config.json"):
|
||||
"""Initialize the configuration manager."""
|
||||
self.config_file = Path(config_file)
|
||||
self.config = self._load_config()
|
||||
|
||||
def _load_config(self) -> Dict[str, Any]:
|
||||
"""Load configuration from file."""
|
||||
if self.config_file.exists():
|
||||
try:
|
||||
with open(self.config_file, 'r') as f:
|
||||
return json.load(f)
|
||||
except (json.JSONDecodeError, IOError):
|
||||
return {}
|
||||
return {}
|
||||
|
||||
def save_config(self) -> bool:
|
||||
"""Save configuration to file."""
|
||||
try:
|
||||
with open(self.config_file, 'w') as f:
|
||||
json.dump(self.config, f, indent=2)
|
||||
return True
|
||||
except IOError:
|
||||
return False
|
||||
|
||||
def get_token(self) -> Optional[str]:
|
||||
"""Get stored Slack token."""
|
||||
return self.config.get('token')
|
||||
|
||||
def get_channel(self) -> Optional[str]:
|
||||
"""Get stored channel ID."""
|
||||
return self.config.get('channel')
|
||||
|
||||
def set_token(self, token: str) -> None:
|
||||
"""Set and save Slack token."""
|
||||
self.config['token'] = token
|
||||
self.save_config()
|
||||
|
||||
def set_channel(self, channel: str) -> None:
|
||||
"""Set and save channel ID."""
|
||||
self.config['channel'] = channel
|
||||
self.save_config()
|
||||
|
||||
def clear_config(self) -> None:
|
||||
"""Clear all stored configuration."""
|
||||
self.config = {}
|
||||
if self.config_file.exists():
|
||||
self.config_file.unlink()
|
||||
|
||||
|
||||
class SlackPoster:
|
||||
"""Main class for posting messages to Slack."""
|
||||
|
||||
@ -59,6 +116,154 @@ class SlackPoster:
|
||||
raise e
|
||||
except SlackClientError as e:
|
||||
raise e
|
||||
|
||||
def get_channel_history(self, channel: str, limit: int = 10) -> List[dict]:
|
||||
"""
|
||||
Get recent messages from a Slack channel.
|
||||
|
||||
Args:
|
||||
channel: Slack channel ID (e.g., C04ABC123)
|
||||
limit: Number of messages to retrieve (max 1000)
|
||||
|
||||
Returns:
|
||||
List[dict]: List of message objects
|
||||
|
||||
Raises:
|
||||
SlackApiError: If the API call fails
|
||||
SlackClientError: If there's a client error
|
||||
"""
|
||||
try:
|
||||
response = self.client.conversations_history(
|
||||
channel=channel,
|
||||
limit=limit
|
||||
)
|
||||
return response.get('messages', [])
|
||||
except SlackApiError as e:
|
||||
raise e
|
||||
except SlackClientError as e:
|
||||
raise e
|
||||
|
||||
def get_user_info(self, user_id: str) -> dict:
|
||||
"""
|
||||
Get user information by user ID.
|
||||
|
||||
Args:
|
||||
user_id: Slack user ID (e.g., U123ABC456)
|
||||
|
||||
Returns:
|
||||
dict: User information
|
||||
|
||||
Raises:
|
||||
SlackApiError: If the API call fails
|
||||
SlackClientError: If there's a client error
|
||||
"""
|
||||
try:
|
||||
response = self.client.users_info(user=user_id)
|
||||
return response.get('user', {})
|
||||
except SlackApiError as e:
|
||||
raise e
|
||||
except SlackClientError as e:
|
||||
raise e
|
||||
|
||||
|
||||
class MessageReceiver:
|
||||
"""Handles receiving and displaying messages from Slack channels."""
|
||||
|
||||
def __init__(self, slack_poster: SlackPoster, channel: str):
|
||||
"""Initialize the message receiver."""
|
||||
self.slack_poster = slack_poster
|
||||
self.channel = channel
|
||||
self.last_message_timestamp = None
|
||||
self.running = False
|
||||
self.thread = None
|
||||
|
||||
def start_listening(self):
|
||||
"""Start listening for new messages in a separate thread."""
|
||||
if self.running:
|
||||
return
|
||||
|
||||
self.running = True
|
||||
self.thread = threading.Thread(target=self._listen_loop, daemon=True)
|
||||
self.thread.start()
|
||||
|
||||
def stop_listening(self):
|
||||
"""Stop listening for messages."""
|
||||
self.running = False
|
||||
if self.thread:
|
||||
self.thread.join(timeout=1)
|
||||
|
||||
def _listen_loop(self):
|
||||
"""Main listening loop that polls for new messages."""
|
||||
while self.running:
|
||||
try:
|
||||
messages = self.slack_poster.get_channel_history(self.channel, limit=5)
|
||||
|
||||
# Filter out messages we've already seen
|
||||
new_messages = []
|
||||
for msg in reversed(messages): # Process oldest first
|
||||
msg_timestamp = msg.get('ts')
|
||||
if self.last_message_timestamp is None or float(msg_timestamp) > float(self.last_message_timestamp):
|
||||
new_messages.append(msg)
|
||||
|
||||
# Display new messages
|
||||
for msg in new_messages:
|
||||
self._display_message(msg)
|
||||
if self.last_message_timestamp is None or float(msg.get('ts')) > float(self.last_message_timestamp):
|
||||
self.last_message_timestamp = msg.get('ts')
|
||||
|
||||
time.sleep(2) # Poll every 2 seconds
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to receive messages: {e}")
|
||||
time.sleep(5) # Wait longer on error
|
||||
|
||||
def _display_message(self, msg: dict):
|
||||
"""Display a received message."""
|
||||
try:
|
||||
user_id = msg.get('user', '')
|
||||
text = msg.get('text', '')
|
||||
timestamp = msg.get('ts', '')
|
||||
|
||||
# Get user info
|
||||
user_name = 'Unknown'
|
||||
if user_id:
|
||||
try:
|
||||
user_info = self.slack_poster.get_user_info(user_id)
|
||||
user_name = user_info.get('real_name', user_info.get('name', user_id))
|
||||
except:
|
||||
user_name = user_id
|
||||
|
||||
# Format timestamp
|
||||
try:
|
||||
dt = datetime.fromtimestamp(float(timestamp))
|
||||
formatted_time = dt.strftime('%H:%M:%S')
|
||||
except (ValueError, TypeError):
|
||||
formatted_time = timestamp
|
||||
|
||||
# Display the message
|
||||
print(f"\n[{formatted_time}] {user_name}: {text}")
|
||||
print("> ", end="", flush=True) # Restore prompt
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to display message: {e}")
|
||||
|
||||
def get_recent_messages(self, limit: int = 10):
|
||||
"""Get and display recent messages from the channel."""
|
||||
try:
|
||||
messages = self.slack_poster.get_channel_history(self.channel, limit=limit)
|
||||
|
||||
print(f"\n[RECENT MESSAGES] Last {len(messages)} messages in #{self.channel}:")
|
||||
print("-" * 60)
|
||||
|
||||
for msg in reversed(messages): # Show newest first
|
||||
self._display_message(msg)
|
||||
|
||||
print("-" * 60)
|
||||
print("> ", end="", flush=True)
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to get recent messages: {e}")
|
||||
print("> ", end="", flush=True)
|
||||
|
||||
|
||||
def get_user_input(prompt: str, required: bool = True) -> str:
|
||||
@ -114,16 +319,279 @@ def validate_channel(channel: str) -> bool:
|
||||
return channel.startswith(('C', 'D', 'G')) and len(channel) > 1
|
||||
|
||||
|
||||
def print_help():
|
||||
"""Print help information for interactive mode."""
|
||||
print("\n" + "="*70)
|
||||
print("SLACK TWO-WAY CHAT MODE - Available Commands:")
|
||||
print("="*70)
|
||||
print(" /help, /h - Show this help message")
|
||||
print(" /quit, /q, /exit - Exit the chat")
|
||||
print(" /token <token> - Set/update Slack token")
|
||||
print(" /channel <id> - Set/update channel ID")
|
||||
print(" /status - Show current configuration")
|
||||
print(" /clear - Clear saved configuration")
|
||||
print(" /test - Test current configuration")
|
||||
print(" /listen - Start listening for incoming messages")
|
||||
print(" /stop - Stop listening for messages")
|
||||
print(" /history [n] - Show recent message history (default: 10)")
|
||||
print(" <message> - Send message to current channel")
|
||||
print("="*70)
|
||||
print("TWO-WAY FEATURES:")
|
||||
print(" - Messages sent by others will appear automatically when listening")
|
||||
print(" - Use /listen to start receiving messages in real-time")
|
||||
print(" - Use /history to see recent conversation")
|
||||
print("="*70)
|
||||
|
||||
|
||||
def interactive_chat_mode(config_manager: ConfigManager):
|
||||
"""Run interactive chat mode."""
|
||||
print("\n[CHAT] Welcome to Slack Two-Way Interactive Chat Mode!")
|
||||
print("Type your messages to send them to Slack.")
|
||||
print("Use /help for available commands.\n")
|
||||
|
||||
# Check if we have stored configuration (try environment variables first, then config file)
|
||||
token = os.getenv('SLACK_BOT_TOKEN') or config_manager.get_token()
|
||||
channel = os.getenv('SLACK_CHANNEL_ID') or config_manager.get_channel()
|
||||
|
||||
if not token:
|
||||
print("No Slack token found. Please set one using /token <your-token>")
|
||||
elif not channel:
|
||||
print("No channel ID found. Please set one using /channel <channel-id>")
|
||||
else:
|
||||
print(f"[OK] Using configuration:")
|
||||
print(f" Channel: {channel}")
|
||||
print(f" Token: {token[:10]}...")
|
||||
|
||||
print("\nReady to chat! Type your message or use /help for commands.")
|
||||
print("Use /listen to start receiving messages from others.\n")
|
||||
|
||||
poster = None
|
||||
message_receiver = None
|
||||
|
||||
while True:
|
||||
try:
|
||||
user_input = input("> ").strip()
|
||||
|
||||
if not user_input:
|
||||
continue
|
||||
|
||||
# Handle commands
|
||||
if user_input.startswith('/'):
|
||||
command = user_input.lower().split()[0]
|
||||
|
||||
if command in ['/help', '/h']:
|
||||
print_help()
|
||||
continue
|
||||
|
||||
elif command in ['/quit', '/q', '/exit']:
|
||||
print("\n[BYE] Goodbye!")
|
||||
break
|
||||
|
||||
elif command == '/token':
|
||||
parts = user_input.split(' ', 1)
|
||||
if len(parts) < 2:
|
||||
print("[ERROR] Usage: /token <your-slack-token>")
|
||||
continue
|
||||
|
||||
new_token = parts[1].strip()
|
||||
if validate_token(new_token):
|
||||
config_manager.set_token(new_token)
|
||||
token = new_token
|
||||
poster = None # Reset poster to use new token
|
||||
print("[OK] Token updated successfully!")
|
||||
else:
|
||||
print("[ERROR] Invalid token format. Tokens should start with 'xoxb-', 'xoxp-', 'xoxa-', or 'xoxr-'")
|
||||
continue
|
||||
|
||||
elif command == '/channel':
|
||||
parts = user_input.split(' ', 1)
|
||||
if len(parts) < 2:
|
||||
print("[ERROR] Usage: /channel <channel-id>")
|
||||
continue
|
||||
|
||||
new_channel = parts[1].strip()
|
||||
if validate_channel(new_channel):
|
||||
config_manager.set_channel(new_channel)
|
||||
channel = new_channel
|
||||
print(f"[OK] Channel updated to: {channel}")
|
||||
else:
|
||||
print("[ERROR] Invalid channel format. Channel IDs should start with 'C', 'D', or 'G'")
|
||||
continue
|
||||
|
||||
elif command == '/status':
|
||||
print(f"\n[STATUS] Current Configuration:")
|
||||
print(f" Token: {token[:10] + '...' if token else 'Not set'}")
|
||||
print(f" Channel: {channel if channel else 'Not set'}")
|
||||
continue
|
||||
|
||||
elif command == '/clear':
|
||||
confirm = input("[WARNING] Are you sure you want to clear all saved configuration? (y/N): ").strip().lower()
|
||||
if confirm in ['y', 'yes']:
|
||||
config_manager.clear_config()
|
||||
token = None
|
||||
channel = None
|
||||
poster = None
|
||||
print("[OK] Configuration cleared!")
|
||||
else:
|
||||
print("[CANCELLED] Configuration not cleared.")
|
||||
continue
|
||||
|
||||
elif command == '/test':
|
||||
if not token or not channel:
|
||||
print("[ERROR] Please set both token and channel before testing.")
|
||||
continue
|
||||
|
||||
try:
|
||||
if not poster:
|
||||
poster = SlackPoster(token)
|
||||
test_message = "[TEST] Test message from Slack Chat CLI"
|
||||
response = poster.post_message(channel, test_message)
|
||||
timestamp = response.get('ts', 'unknown')
|
||||
|
||||
# Convert timestamp to readable format
|
||||
try:
|
||||
dt = datetime.fromtimestamp(float(timestamp))
|
||||
formatted_time = dt.strftime('%H:%M:%S')
|
||||
except (ValueError, TypeError):
|
||||
formatted_time = timestamp
|
||||
|
||||
# Display the test message
|
||||
print(f"[{formatted_time}] You: {test_message}")
|
||||
print("[OK] Test message sent successfully!")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Test failed: {e}")
|
||||
continue
|
||||
|
||||
elif command == '/listen':
|
||||
if not token or not channel:
|
||||
print("[ERROR] Please set both token and channel before listening.")
|
||||
continue
|
||||
|
||||
try:
|
||||
if not poster:
|
||||
poster = SlackPoster(token)
|
||||
|
||||
if message_receiver and message_receiver.running:
|
||||
print("[INFO] Already listening for messages.")
|
||||
else:
|
||||
message_receiver = MessageReceiver(poster, channel)
|
||||
message_receiver.start_listening()
|
||||
print("[OK] Started listening for incoming messages!")
|
||||
print(" Messages from others will appear automatically.")
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to start listening: {e}")
|
||||
continue
|
||||
|
||||
elif command == '/stop':
|
||||
if message_receiver and message_receiver.running:
|
||||
message_receiver.stop_listening()
|
||||
print("[OK] Stopped listening for messages.")
|
||||
else:
|
||||
print("[INFO] Not currently listening for messages.")
|
||||
continue
|
||||
|
||||
elif command == '/history':
|
||||
if not token or not channel:
|
||||
print("[ERROR] Please set both token and channel before viewing history.")
|
||||
continue
|
||||
|
||||
try:
|
||||
if not poster:
|
||||
poster = SlackPoster(token)
|
||||
|
||||
# Parse limit if provided
|
||||
parts = user_input.split()
|
||||
limit = 10
|
||||
if len(parts) > 1:
|
||||
try:
|
||||
limit = int(parts[1])
|
||||
limit = min(limit, 50) # Cap at 50 for performance
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if not message_receiver:
|
||||
message_receiver = MessageReceiver(poster, channel)
|
||||
message_receiver.get_recent_messages(limit)
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to get message history: {e}")
|
||||
continue
|
||||
|
||||
else:
|
||||
print(f"[ERROR] Unknown command: {command}. Use /help for available commands.")
|
||||
continue
|
||||
|
||||
# Send message
|
||||
if not token or not channel:
|
||||
print("[ERROR] Please set both token and channel before sending messages.")
|
||||
print(" Use /token <your-token> and /channel <channel-id>")
|
||||
continue
|
||||
|
||||
try:
|
||||
if not poster:
|
||||
poster = SlackPoster(token)
|
||||
|
||||
response = poster.post_message(channel, user_input)
|
||||
timestamp = response.get('ts', 'unknown')
|
||||
|
||||
# Convert timestamp to readable format
|
||||
try:
|
||||
dt = datetime.fromtimestamp(float(timestamp))
|
||||
formatted_time = dt.strftime('%H:%M:%S')
|
||||
except (ValueError, TypeError):
|
||||
formatted_time = timestamp
|
||||
|
||||
# Display the sent message in the terminal
|
||||
print(f"[{formatted_time}] You: {user_input}")
|
||||
print(f"[OK] Message sent to #{channel}")
|
||||
|
||||
except SlackApiError as e:
|
||||
error_msg = str(e)
|
||||
if "invalid_auth" in error_msg.lower():
|
||||
print("[ERROR] Invalid token. Please update your token using /token <new-token>")
|
||||
elif "channel_not_found" in error_msg.lower():
|
||||
print(f"[ERROR] Channel '{channel}' not found. Please check the channel ID.")
|
||||
elif "not_in_channel" in error_msg.lower():
|
||||
print(f"[ERROR] Bot is not a member of channel '{channel}'. Please add the bot to the channel.")
|
||||
elif "rate_limited" in error_msg.lower():
|
||||
print("[ERROR] Rate limited. Please wait before trying again.")
|
||||
else:
|
||||
print(f"[ERROR] Slack API Error: {error_msg}")
|
||||
|
||||
except SlackClientError as e:
|
||||
print(f"[ERROR] Slack Client Error: {e}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Unexpected error: {e}")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n[BYE] Goodbye!")
|
||||
if message_receiver:
|
||||
message_receiver.stop_listening()
|
||||
break
|
||||
except EOFError:
|
||||
print("\n\n[BYE] Goodbye!")
|
||||
if message_receiver:
|
||||
message_receiver.stop_listening()
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to handle CLI arguments and execute the message posting."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Post a message to a Slack channel",
|
||||
description="Post a message to a Slack channel or start interactive chat mode",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# CLI Mode - Send a single message
|
||||
python slack_poster.py -t xoxb-1234567890-1234567890-abcdefghijklmnopqrstuvwx -c C04ABC123 -m "Hello World!"
|
||||
python slack_poster.py --token xoxb-1234567890-1234567890-abcdefghijklmnopqrstuvwx --channel C04ABC123 --message "Hello World!"
|
||||
python slack_poster.py # Interactive mode - will prompt for missing parameters
|
||||
|
||||
# Interactive Chat Mode
|
||||
python slack_poster.py --chat
|
||||
python slack_poster.py -i
|
||||
|
||||
# CLI Mode with prompts for missing parameters
|
||||
python slack_poster.py
|
||||
"""
|
||||
)
|
||||
|
||||
@ -145,24 +613,43 @@ Examples:
|
||||
help='Message text to post'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--chat', '-i', '--interactive',
|
||||
action='store_true',
|
||||
help='Start interactive chat mode'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get token (try environment variable first, then command line, then interactive)
|
||||
token = args.token or os.getenv('SLACK_BOT_TOKEN')
|
||||
# Initialize configuration manager
|
||||
config_manager = ConfigManager()
|
||||
|
||||
# If interactive chat mode is requested
|
||||
if args.chat:
|
||||
interactive_chat_mode(config_manager)
|
||||
return
|
||||
|
||||
# CLI Mode - Send a single message
|
||||
# Get token (try environment variable first, then command line, then config, then interactive)
|
||||
token = args.token or os.getenv('SLACK_BOT_TOKEN') or config_manager.get_token()
|
||||
if not token:
|
||||
print("Slack Token not provided. Please enter it interactively:")
|
||||
token = get_user_input("Enter your Slack token: ")
|
||||
# Save token for future use
|
||||
config_manager.set_token(token)
|
||||
|
||||
# Validate token format
|
||||
if not validate_token(token):
|
||||
print("Error: Invalid token format. Slack tokens typically start with 'xoxb-', 'xoxp-', 'xoxa-', or 'xoxr-'")
|
||||
sys.exit(1)
|
||||
|
||||
# Get channel (try environment variable first, then command line, then interactive)
|
||||
channel = args.channel or os.getenv('SLACK_CHANNEL_ID')
|
||||
# Get channel (try environment variable first, then command line, then config, then interactive)
|
||||
channel = args.channel or os.getenv('SLACK_CHANNEL_ID') or config_manager.get_channel()
|
||||
if not channel:
|
||||
print("Channel ID not provided. Please enter it interactively:")
|
||||
channel = get_user_input("Enter Slack channel ID (e.g., C04ABC123): ")
|
||||
# Save channel for future use
|
||||
config_manager.set_channel(channel)
|
||||
|
||||
# Validate channel format
|
||||
if not validate_channel(channel):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user