Ipcam Telegram Group Info

  • Example: Telegram motion-alert bot (high level)
  • Example: Minimizing exposure when using DDNS
  • logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(name)

    class IPCameraTelegramMonitor: """ Monitor IP cameras through Telegram group with features like: - Motion detection - Snapshot capture - Recording management - Face detection (optional) """

    def __init__(self, config_path: str = "config.json"):
        """
        Initialize the IP camera Telegram monitor.
    Args:
            config_path: Path to configuration file
        """
        self.config = self.load_config(config_path)
        self.bot_token = self.config["telegram_bot_token"]
        self.allowed_group_id = self.config["telegram_group_id"]
        self.ip_cameras = self.config["ip_cameras"]
    # Storage paths
        self.media_path = Path(self.config.get("media_storage_path", "./camera_media"))
        self.media_path.mkdir(exist_ok=True)
    # Motion detection settings
        self.motion_threshold = self.config.get("motion_threshold", 25)
        self.motion_history = {}
    def load_config(self, config_path: str) -> Dict:
        """Load configuration from JSON file."""
        default_config = 
            "telegram_bot_token": "YOUR_BOT_TOKEN",
            "telegram_group_id": -1001234567890,
            "media_storage_path": "./camera_media",
            "motion_threshold": 25,
            "ip_cameras": [
    "name": "Front Door",
                    "url": "http://192.168.1.100/video",
                    "snapshot_url": "http://192.168.1.100/snapshot",
                    "enabled": True
                ,
    "name": "Backyard",
                    "url": "http://192.168.1.101/video",
                    "snapshot_url": "http://192.168.1.101/snapshot",
                    "enabled": True
    ]
    try:
            with open(config_path, 'r') as f:
                config = json.load(f)
                # Merge with defaults for missing keys
                for key, value in default_config.items():
                    if key not in config:
                        config[key] = value
                return config
        except FileNotFoundError:
            logger.warning(f"Config file config_path not found. Using defaults.")
            return default_config
    async def handle_group_messages(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Handle messages from the Telegram group."""
        if not update.message or not update.message.chat:
            return
    chat_id = update.message.chat.id
    # Check if message is from allowed group
        if chat_id != self.allowed_group_id:
            return
    message_text = update.message.text if update.message.text else ""
        message_lower = message_text.lower()
    # Handle commands from group
        if message_text.startswith('/'):
            await self.handle_commands(update, context)
        elif any(cmd in message_lower for cmd in ['photo', 'snap', 'capture']):
            await self.capture_and_send_snapshot(update, context)
        elif any(cmd in message_lower for cmd in ['video', 'record', 'stream']):
            await self.start_video_preview(update, context)
        elif 'status' in message_lower:
            await self.send_status(update, context)
        elif 'help' in message_lower:
            await self.send_help(update, context)
    async def handle_commands(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Handle slash commands."""
        commands = 
            '/start': self.start_command,
            '/help': self.help_command,
            '/snapshot': self.snapshot_command,
            '/status': self.status_command,
            '/record': self.record_command,
            '/motion_on': self.enable_motion_detection,
            '/motion_off': self.disable_motion_detection,
            '/settings': self.show_settings
    if update.message.text:
            cmd = update.message.text.split()[0].lower()
            if cmd in commands:
                await commands[cmd](update, context)
    async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Handle /start command."""
        await update.message.reply_text(
            "🎥 IP Camera Monitor Active!\n\n"
            "Available commands:\n"
            "/snapshot - Take a snapshot from all cameras\n"
            "/status - Check camera status\n"
            "/record - Start/stop recording\n"
            "/motion_on - Enable motion detection\n"
            "/motion_off - Disable motion detection\n"
            "/help - Show all commands"
        )
    async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Show help information."""
        help_text = """
    

    🤖 IP Camera Bot Commands

    Camera Controls:/snapshot [camera_name] - Take a snapshot • /status - Check camera status • /record [duration] - Record video (duration in seconds)

    Motion Detection:/motion_on - Enable motion detection • /motion_off - Disable motion detection

    Settings:/settings - View current settings

    Examples:/snapshot Front Door - Capture from specific camera • /record 30 - Record for 30 seconds • /motion_on - Start motion detection ipcam telegram group

    Reply with camera names or leave blank for all cameras """ await update.message.reply_text(help_text, parse_mode='Markdown')

    async def snapshot_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Handle /snapshot command."""
        args = context.args
        camera_name = ' '.join(args) if args else None
    await update.message.reply_text("📸 Capturing snapshot...")
    if camera_name:
            # Capture from specific camera
            camera = next((c for c in self.ip_cameras if c['name'].lower() == camera_name.lower()), None)
            if camera and camera['enabled']:
                await self.capture_and_send_camera(update, camera)
            else:
                await update.message.reply_text(f"❌ Camera 'camera_name' not found or disabled")
        else:
            # Capture from all cameras
            tasks = [self.capture_and_send_camera(update, camera) 
                    for camera in self.ip_cameras if camera['enabled']]
            await asyncio.gather(*tasks)
    async def capture_and_send_camera(self, update: Update, camera: Dict):
        """Capture and send snapshot from a specific camera."""
        try:
            snapshot_url = camera.get('snapshot_url')
            if not snapshot_url:
                await update.message.reply_text(f"❌ camera['name']: No snapshot URL configured")
                return
    # Fetch image from camera
            response = requests.get(snapshot_url, timeout=10)
            response.raise_for_status()
    # Save snapshot locally
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"camera['name']_timestamp.jpg"
            filepath = self.media_path / filename
    with open(filepath, 'wb') as f:
                f.write(response.content)
    # Send to Telegram
            await update.message.reply_photo(
                photo=open(filepath, 'rb'),
                caption=f"📷 camera['name']\n🕐 datetime.now().strftime('%Y-%m-%d %H:%M:%S')"
            )
    logger.info(f"Snapshot captured from camera['name']")
    except requests.RequestException as e:
            logger.error(f"Failed to capture from camera['name']: e")
            await update.message.reply_text(f"❌ camera['name']: Failed to capture snapshot")
    async def capture_and_send_snapshot(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Capture and send snapshots from all enabled cameras."""
        await self.snapshot_command(update, context)
    async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Send camera status."""
        await self.send_status(update, context)
    async def send_status(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Send detailed status of all cameras."""
        status_text = "📊 **Camera Status**\n\n"
    for camera in self.ip_cameras:
            status_icon = "✅" if camera['enabled'] else "❌"
            status_text += f"status_icon **camera['name']**\n"
            status_text += f"   URL: camera['url']\n"
            status_text += f"   Enabled: camera['enabled']\n\n"
    status_text += f"📁 Storage: self.media_path.absolute()\n"
        status_text += f"🎯 Motion Threshold: self.motion_threshold"
    await update.message.reply_text(status_text, parse_mode='Markdown')
    async def record_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Handle video recording command."""
        args = context.args
        duration = int(args[0]) if args and args[0].isdigit() else 10
    await update.message.reply_text(f"🎥 Recording started for duration seconds...")
    # Record from all cameras
        for camera in self.ip_cameras:
            if camera['enabled']:
                asyncio.create_task(self.record_from_camera(update, camera, duration))
    async def record_from_camera(self, update: Update, camera: Dict, duration: int):
        """Record video from a camera stream."""
        try:
            cap = cv2.VideoCapture(camera['url'])
            if not cap.isOpened():
                await update.message.reply_text(f"❌ camera['name']: Cannot access stream")
                return
    # Setup video writer
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"camera['name']_timestamp.mp4"
            filepath = self.media_path / filename
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            fps = 20
            frame_width = int(cap.get(3))
            frame_height = int(cap.get(4))
            out = cv2.VideoWriter(str(filepath), fourcc, fps, (frame_width, frame_height))
    # Record for specified duration
            start_time = asyncio.get_event_loop().time()
            frames_written = 0
    while (asyncio.get_event_loop().time() - start_time) < duration:
                ret, frame = cap.read()
                if ret:
                    out.write(frame)
                    frames_written += 1
                await asyncio.sleep(1/fps)
    cap.release()
            out.release()
    # Send video to Telegram
            if filepath.exists() and filepath.stat().st_size > 0:
                with open(filepath, 'rb') as video_file:
                    await update.message.reply_video(
                        video=video_file,
                        caption=f"🎬 camera['name']\n⏱️ Duration: durations\n📅 datetime.now().strftime('%Y-%m-%d %H:%M:%S')"
                    )
                logger.info(f"Recording saved from camera['name']: frames_written frames")
            else:
                await update.message.reply_text(f"❌ camera['name']: Recording failed")
    except Exception as e:
            logger.error(f"Recording error for camera['name']: e")
            await update.message.reply_text(f"❌ camera['name']: Recording error")
    async def enable_motion_detection(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Enable motion detection for cameras."""
        # Start motion detection tasks for each camera
        self.motion_detection_tasks = []
        for camera in self.ip_cameras:
            if camera['enabled']:
                task = asyncio.create_task(self.motion_detection_loop(update, camera))
                self.motion_detection_tasks.append(task)
    await update.message.reply_text("🔍 Motion detection enabled for all cameras")
    async def disable_motion_detection(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Disable motion detection."""
        if hasattr(self, 'motion_detection_tasks'):
            for task in self.motion_detection_tasks:
                task.cancel()
            await update.message.reply_text("🔍 Motion detection disabled")
    async def motion_detection_loop(self, update: Update, camera: Dict):
        """Continuous motion detection loop for a camera."""
        cap = cv2.VideoCapture(camera['url'])
        if not cap.isOpened():
            logger.error(f"Cannot open stream for camera['name']")
            return
    # Initialize background subtractor
        fgbg = cv2.createBackgroundSubtractorMOG2()
        last_motion_time = 0
        cooldown_seconds = 10  # Avoid spam
    try:
            while True:
                ret, frame = cap.read()
                if not ret:
                    await asyncio.sleep(1)
                    continue
    # Apply background subtraction
                fgmask = fgbg.apply(frame)
    # Calculate motion area
                motion_area = np.sum(fgmask > 0)
                frame_area = frame.shape[0] * frame.shape[1]
                motion_percentage = (motion_area / frame_area) * 100
    # Check if motion detected
                current_time = asyncio.get_event_loop().time()
                if motion_percentage > self.motion_threshold and (current_time - last_motion_time) > cooldown_seconds:
                    last_motion_time = current_time
    # Save motion frame
                    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
                    filename = f"motion_camera['name']_timestamp.jpg"
                    filepath = self.media_path / filename
                    cv2.imwrite(str(filepath), frame)
    # Send alert to Telegram
                    await update.message.reply_photo(
                        photo=open(filepath, 'rb'),
                        caption=f"⚠️ Motion detected on camera['name']!\n"
                               f"📊 Motion: motion_percentage:.1f%\n"
                               f"🕐 datetime.now().strftime('%Y-%m-%d %H:%M:%S')"
                    )
    logger.info(f"Motion detected on camera['name']: motion_percentage:.1f%")
    await asyncio.sleep(0.1)  # Check every 100ms
    except asyncio.CancelledError:
            logger.info(f"Motion detection stopped for camera['name']")
        finally:
            cap.release()
    async def show_settings(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
        """Show current settings."""
        settings_text = """
    

    ⚙️ Current Settings

    Motion Detection: • Threshold: {}

    Storage: • Path: {}

    Cameras: • Total: {} • Enabled: {}/{} """.strip().format( self.motion_threshold, self.media_path, len(self.ip_cameras), sum(1 for c in self.ip_cameras if c['enabled']), len(self.ip_cameras) )

        await update.message.reply_text(settings_text, parse_mode='Markdown')
    async def run(self):
        """Main function to run the bot."""
        # Create application
        application = Application.builder().token(self.bot_token).build()
    # Add handlers
        application.add_handler(MessageHandler(
            filters.TEXT & ~filters.COMMAND,
            self.handle_group_messages
        ))
        application.add_handler(CommandHandler('start', self.start_command))
        application.add_handler(CommandHandler('help', self.help_command))
        application.add_handler(CommandHandler('snapshot', self.snapshot_command))
        application.add_handler(CommandHandler('status', self.status_command))
        application.add_handler(CommandHandler('record', self.record_command))
        application.add_handler(CommandHandler('motion_on', self.enable_motion_detection))
        application.add_handler(CommandHandler('motion_off', self.disable_motion_detection))
        application.add_handler(CommandHandler('settings', self.show_settings))
    # Start the bot
        logger.info("Starting IP Camera Telegram Monitor...")
        await application.initialize()
        await application.start()
        await application.updater.start_polling()
    # Keep running
        try:
            await asyncio.Event().wait()
        except KeyboardInterrupt:
            logger.info("Shutting down...")
            await application.updater.stop()
            await application.stop()
            await application.shutdown()
    

    The concept is simple, yet terrifying. Internet Protocol (IP) cameras—those sleek, white domes and orbs sold by Amazon Ring, Nest, Wyze, and hundreds of cheaper, generic manufacturers—are designed to offer homeowners peace of mind. They allow a parent to check on a sleeping baby from the office, or a homeowner to see who is at the door from miles away. Example: Telegram motion-alert bot (high level)

    However, the convenience of "plug-and-play" often comes at the cost of security. To make setup easy for the average consumer, many devices ship with default usernames and passwords (often as simple as "admin/admin" or "12345"). When users fail to change these, their cameras become open doors.

    Telegram groups dedicated to these vulnerabilities are the digital equivalent of a peeping tom’s convention, but on a global scale. In these channels, thousands of members do not trade stolen credit card numbers or hacked accounts. They trade lives.

    A typical feed in one of these groups is a collage of the mundane and the intimate. A grainy, green-tinted night vision shot of a couple sleeping. A high-definition view of a receptionist’s desk in a dental clinic in Brazil. A chaotic feed of children playing in a nursery in Ohio.

    The posts are rarely captioned with context. They are simply labeled with a location—a country, sometimes a city—and occasionally a mocking note: "Nice kitchen," or "She doesn't know."

    if name == "main": import sys

    if len(sys.argv) > 1 and sys.argv[1] == "setup":
        create_config_template()
        create_requirements()
        print("\n📋 Setup complete! Follow these steps:")
        print("1. Edit config.json with your camera details")
        print("2. Install requirements: pip install -r requirements.txt")
        print("3. Run: python ipcam_monitor.py")
    else:
        # Run the monitor
        monitor = IPCameraTelegramMonitor()
        asyncio.run(monitor.run())
    

    You can find them via:


    📷 *Welcome to the IPCam Enthusiasts Group* 📷
    

    🔒 Rules:

    📌 Useful resources:

    👥 Share:

    👉 Invite link: [INSERT YOUR GROUP LINK]

    🚨 Admin: @username


    Partners