File size: 5,270 Bytes
8490d1b
511ba56
231072a
511ba56
 
 
 
109d656
5d10ce5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109d656
8490d1b
 
 
 
 
 
 
 
 
 
 
 
 
 
2147d1c
e72602f
 
2147d1c
8490d1b
 
2147d1c
e72602f
 
 
 
 
2147d1c
e72602f
e9bfcb7
2147d1c
e72602f
2147d1c
e9bfcb7
2147d1c
e72602f
75fe874
7891d5f
 
 
5d10ce5
 
 
 
 
8619a68
5d10ce5
8619a68
5d10ce5
 
 
 
231072a
8619a68
5d10ce5
 
 
231072a
5d10ce5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231072a
5d10ce5
75fe874
 
7891d5f
e72602f
 
2147d1c
e72602f
5d10ce5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from pydantic import BaseModel
import httpx
import json
from config import TELEGRAM_URL
from ai_service import get_ai_response
from database import db_manager

TELEGRAM_IP = "149.154.167.220"
MAX_TELEGRAM_MESSAGE_LENGTH = 4096


def _sanitize_telegram_text(text: str) -> str:
    if text is None:
        return ""

    normalized = str(text).replace("\r\n", "\n").replace("\r", "\n")

    # Remove control/surrogate chars that Telegram can reject.
    cleaned = "".join(
        ch
        for ch in normalized
        if (ch in ("\n", "\t") or ord(ch) >= 32) and not (0xD800 <= ord(ch) <= 0xDFFF)
    )
    return cleaned.strip()


def _split_telegram_message(text: str, max_length: int = MAX_TELEGRAM_MESSAGE_LENGTH):
    if len(text) <= max_length:
        return [text]

    parts = []
    remaining = text

    while len(remaining) > max_length:
        split_at = remaining.rfind("\n", 0, max_length)
        if split_at == -1:
            split_at = remaining.rfind(" ", 0, max_length)
        if split_at == -1:
            split_at = max_length

        part = remaining[:split_at].strip()
        if not part:
            part = remaining[:max_length]
            split_at = max_length

        parts.append(part)
        remaining = remaining[split_at:].strip()

    if remaining:
        parts.append(remaining)

    return parts

class ChatInfo(BaseModel):
    id: int
    username: str = None
    first_name: str = None
    last_name: str = None

class Message(BaseModel):
    chat: ChatInfo
    text: str = ""

class WebhookData(BaseModel):
    message: Message = None

async def telegram_webhook(data: WebhookData):
    try:
        if not data.message or not data.message.text:
            return {"status": "ok"}

        telegram_id = data.message.chat.id
        user_text = data.message.text
        
        username = data.message.chat.username
        first_name = data.message.chat.first_name

        print(f"--- Processing message from {first_name} ---")

        if db_manager:
            db_manager.create_or_update_user(telegram_id, username, first_name, data.message.chat.last_name)

        
        ai_answer = await get_ai_response(user_text, telegram_id)
        

        
        if TELEGRAM_URL:
            try:
                from config import TELEGRAM_TOKEN 
                
                async with httpx.AsyncClient(timeout=40.0, verify=False, follow_redirects=True) as client:
                    prepared_text = _sanitize_telegram_text(
                        ai_answer or "Sorry, I couldn't generate a response. Please try again."
                    )
                    if not prepared_text:
                        prepared_text = "Sorry, I couldn't generate a response. Please try again."

                    message_parts = _split_telegram_message(prepared_text, MAX_TELEGRAM_MESSAGE_LENGTH)

                    for idx, part in enumerate(message_parts, start=1):
                        payload = {
                            "chat_id": telegram_id,
                            "text": part,
                        }

                        print(
                            f"--- Sending Telegram message part {idx}/{len(message_parts)} "
                            f"(chars={len(part)}) ---"
                        )

                        try:
                            # Use form data for maximal compatibility with Telegram endpoint parsers.
                            response = await client.post(TELEGRAM_URL, data=payload)
                        except Exception:
                            print(f"--- DNS Failed. Forcing Direct IP Routing to {TELEGRAM_IP} ---")

                            forced_ip_url = f"https://{TELEGRAM_IP}/bot{TELEGRAM_TOKEN}/sendMessage"
                            json_body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
                            headers = {
                                "Host": "api.telegram.org",
                                "Content-Type": "application/json; charset=utf-8",
                                "Content-Length": str(len(json_body)),
                            }

                            response = await client.post(
                                forced_ip_url,
                                content=json_body,
                                headers=headers,
                            )

                        if response.status_code != 200:
                            print(f"--- Telegram payload rejected (part {idx}) ---")
                            print(f"--- Payload: {payload} ---")
                            print(
                                f"--- Telegram Rejected Request: "
                                f"{response.status_code} - {response.text} ---"
                            )
                            break
                    else:
                        print("--- Success: All Telegram message parts delivered ---")
                        
            except Exception as send_error:
                print(f"--- Emergency: Network Blockage Detected: {str(send_error)} ---")
                    
        return {"status": "ok"}
    except Exception as e:
        print(f"Error in webhook: {str(e)}")
        return {"status": "error", "message": str(e)}