CasperSecurity

Current Path : /opt/hikvison/
Upload File :
Current File : //opt/hikvison/face_recognition.py

import json
import datetime
import logging
from flask import Flask, request, jsonify
from datetime import datetime, timedelta
import paho.mqtt.client as mqtt
import requests  # 👈 Added missing import

# ------------------------
# CONFIGURATION
# ------------------------
MQTT_BROKER = "192.168.0.165"
MQTT_PORT = 1883
MQTT_TOPIC = "hikvision/punch"

MQTT_USERNAME = "mqtt_user"
MQTT_PASSWORD = "P@ssw0rd@2026"

ATTENDANCE_API = "https://hrms.uiet.co.in/api/attendance"

# Store last API hit time for each employee
last_api_calls = {}

# 5-minute cooldown
API_COOLDOWN = timedelta(minutes=5)

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

app = Flask(__name__)

# ------------------------
# MQTT SETUP
# ------------------------
try:
    mqtt_client = mqtt.Client()
    mqtt_client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD) 
    mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60)
    mqtt_client.loop_start()
    logger.info(f"Connected to MQTT Broker at {MQTT_BROKER}:{MQTT_PORT}")
except Exception as e:
    logger.error(f"Failed to connect to MQTT Broker: {e}")
    mqtt_client = None


def parse_hikvision_payload(req):
    """
    Attempts to extract event data from various Hikvision payload formats.
    """
    data = {}

    # 1. Try JSON Body
    if req.is_json:
        logger.info("Payload is JSON")
        data = req.get_json()

    # 2. Try Multipart 'event_log' (Common in newer firmware)
    elif 'event_log' in req.files:
        logger.info("Payload contains 'event_log' file")
        try:
            file_content = req.files['event_log'].read()
            data = json.loads(file_content)
        except Exception as e:
            logger.error(f"Failed to parse event_log file: {e}")

    # 3. Try standard Form Data (sometimes sent as a string in a field)
    elif req.form:
        logger.info("Payload is Form Data")
        data = req.form.to_dict()
        if 'event_log' in data:
            try:
                data = json.loads(data['event_log'])
            except:
                pass

    # 4. Fallback: Try raw data parse
    else:
        try:
            raw = req.get_data()
            if raw:
                data = json.loads(raw)
        except:
            pass

    return data


def call_attendance_api(employee_id, date_time):
    try:
        payload = {
            "employee_id": employee_id,
            "date_time": date_time.replace("T", " ")[:19]
        }

        logger.info(f"Sending Attendance: {payload}")

        response = requests.post(
            ATTENDANCE_API,
            json=payload,
            timeout=10
        )

        logger.info(
            f"Attendance API Response: {response.status_code} | {response.text}"
        )

        return response.status_code == 200

    except Exception as e:
        logger.exception(f"Attendance API Error: {e}")
        return False

def should_call_api(employee_id):
    now = datetime.now()

    if employee_id not in last_api_calls:
        last_api_calls[employee_id] = now
        return True

    elapsed = now - last_api_calls[employee_id]

    if elapsed >= API_COOLDOWN:
        last_api_calls[employee_id] = now
        return True

    return False

def extract_punch_data(data):
    try:
        if isinstance(data, str):
            data = json.loads(data)

        if not isinstance(data, dict):
            logger.error(f"Expected dict, got {type(data)}")
            return None

        # Get AccessControllerEvent block (can be a dict or json string)
        outer = data.get("AccessControllerEvent")

        if isinstance(outer, str):
            outer = json.loads(outer)

        if not isinstance(outer, dict):
            logger.error(f"Outer is {type(outer)}")
            return None

        # Fix: Safely handle both single-nested and double-nested structures
        if "AccessControllerEvent" in outer:
            event = outer["AccessControllerEvent"]
            if isinstance(event, str):
                event = json.loads(event)
        else:
            event = outer

        if not isinstance(event, dict):
            logger.error(f"Event is {type(event)}")
            return None

        # Fix: Search for dateTime at the root level first, then fallback
        dateTimeStr = data.get("dateTime") or outer.get("dateTime") or event.get("dateTime")

        return {
            "employee_id": event.get("employeeNoString"),
            "name": event.get("name"),
            "verify_mode": event.get("currentVerifyMode"),
            "major_event": event.get("majorEventType"),
            "sub_event": event.get("subEventType") or event.get("minorEventType"),
            "card_reader": event.get("cardReaderNo"),
            "user_type": event.get("userType"),
            "mask": event.get("mask"),
            "date_time": dateTimeStr,
            "device_ip": data.get("ipAddress") or event.get("ipAddress"),
            "serial_no": event.get("serialNo"),
            "received_at": datetime.now().isoformat()
        }

    except Exception as e:
        logger.exception(f"Error extracting punch data: {e}")
        return None

# ------------------------
# ENDPOINT
# ------------------------
@app.route("/hikvision/event", methods=["POST"])
def hik_event():
    try:
        # 1. Parse Data
        raw_data = parse_hikvision_payload(request)
        logger.info(f"📥 Received Raw Data Structure: {json.dumps(raw_data, indent=2, default=str)}")

        # 2. Extract Fields
        punch = extract_punch_data(raw_data)

        if punch:
            logger.info(f"✅ Extracted Punch: {punch}")
            
            employee_id = punch.get("employee_id")
            if employee_id:
                if should_call_api(employee_id):
                    logger.info(f"📡 Calling Attendance API for Employee {employee_id}")
                    success = call_attendance_api(employee_id, punch["date_time"])

                    if success:
                        logger.info(f"✅ Attendance API Success for Employee {employee_id}")
                    else:
                        logger.warning(f"❌ Attendance API Failed for Employee {employee_id}")
                else:
                    logger.info(
                        f"⏳ Skipping Employee {employee_id}. "
                        "Attendance already sent within last 5 minutes."
                    )
            else:
                logger.warning("Employee ID not found. Skipping Attendance API.")
            
            # 3. Publish to MQTT
            if mqtt_client:
                mqtt_client.publish(MQTT_TOPIC, json.dumps(punch), qos=1)
            else:
                logger.warning("MQTT not connected, skipping publish")

            return "OK", 200
        else:
            logger.warning("⚠️ Could not extract valid punch data from payload.")
            return "OK", 200

    except Exception as e:
        logger.error(f"⚠️ Error processing event: {e}")
        return "OK", 200


# ------------------------
# HEALTH CHECK
# ------------------------
@app.route("/health")
def health():
    return {"status": "running"}


if __name__ == "__main__":
    logger.info("🚀 Robust Hikvision Listener started on port 8000")
    app.run(host="0.0.0.0", port=8000)
Hacker Blog, Shell İndir, Sql İnjection, XSS Attacks, LFI Attacks, Social Hacking, Exploit Bot, Proxy Tools, Web Shell, PHP Shell, Alfa Shell İndir, Hacking Training Set, DDoS Script, Denial Of Service, Botnet, RFI Attacks, Encryption
Telegram @BIBIL_0DAY