File size: 7,283 Bytes
1a1e2eb
 
2fe1440
 
 
1a1e2eb
3a67739
1a1e2eb
1fa270a
9a92ff5
746a920
1a1e2eb
 
9a92ff5
1a1e2eb
 
9a92ff5
 
2fe1440
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3a67739
 
1fa270a
 
 
 
 
3a67739
 
 
 
1fa270a
 
 
 
 
 
 
 
 
29b5c59
1fa270a
29b5c59
1a1e2eb
3a67739
 
1fa270a
 
 
 
 
 
 
 
 
 
 
 
 
1a1e2eb
3a67739
 
 
658e097
 
3a67739
29b5c59
1fa270a
 
 
 
 
3a67739
 
 
 
2fe1440
 
 
 
 
 
 
 
 
 
 
 
 
 
1a1e2eb
 
3a67739
 
 
 
 
 
 
 
1fa270a
 
 
 
 
3a67739
1fa270a
 
 
 
 
3a67739
1fa270a
 
 
 
 
29b5c59
1a1e2eb
 
3a67739
1fa270a
 
 
 
e7261d3
 
 
 
 
 
3a67739
 
 
9a92ff5
 
 
1fa270a
 
 
 
 
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# Thunder/utils/bot_utils.py

import asyncio
from typing import Any, Dict, Optional
from urllib.parse import quote

from pyrogram import Client
from pyrogram.enums import ChatMemberStatus
from pyrogram.errors import FloodWait
from pyrogram.types import (InlineKeyboardButton, InlineKeyboardMarkup,
                            Message, User)

from Thunder.utils.database import db
from Thunder.utils.file_properties import get_fname, get_fsize, get_hash
from Thunder.utils.human_readable import humanbytes
from Thunder.utils.logger import logger
from Thunder.utils.messages import (MSG_BUTTON_GET_HELP, MSG_DC_UNKNOWN,
                                    MSG_DC_USER_INFO, MSG_NEW_USER)
from Thunder.utils.shortener import shorten
from Thunder.vars import Var


def quote_media_name(file_name: str) -> str:
    return quote(str(file_name).replace("/", "_"), safe="")


async def _build_links(
    *,
    download_path: str,
    stream_path: str,
    media_name: str,
    media_size: str,
    shortener: bool = True
) -> Dict[str, str]:
    base_url = Var.URL.rstrip("/")
    slink = f"{base_url}{stream_path}"
    olink = f"{base_url}{download_path}"

    if shortener and getattr(Var, "SHORTEN_MEDIA_LINKS", False):
        try:
            s_results = await asyncio.gather(shorten(slink), shorten(olink), return_exceptions=True)
            if not isinstance(s_results[0], Exception):
                slink = s_results[0]
            else:
                logger.warning(f"Failed to shorten stream_link: {s_results[0]}")
            if not isinstance(s_results[1], Exception):
                olink = s_results[1]
            else:
                logger.warning(f"Failed to shorten online_link: {s_results[1]}")
        except Exception as e:
            logger.error(f"Error during link shortening: {e}")

    return {"stream_link": slink, "online_link": olink, "media_name": media_name, "media_size": media_size}


async def gen_canonical_links(
    *,
    file_name: str,
    file_size: int,
    public_hash: str,
    shortener: bool = True
) -> Dict[str, str]:
    media_name = str(file_name)
    media_size = humanbytes(file_size)
    encoded_name = quote_media_name(media_name)
    return await _build_links(
        download_path=f"/f/{public_hash}/{encoded_name}",
        stream_path=f"/watch/f/{public_hash}/{encoded_name}",
        media_name=media_name,
        media_size=media_size,
        shortener=shortener
    )



async def notify_ch(cli: Client, txt: str):
    if not (hasattr(Var, 'BIN_CHANNEL') and isinstance(Var.BIN_CHANNEL, int) and Var.BIN_CHANNEL != 0):
        return
    try:
        await cli.send_message(chat_id=Var.BIN_CHANNEL, text=txt)
    except FloodWait as e:
        await asyncio.sleep(e.value)
        await cli.send_message(chat_id=Var.BIN_CHANNEL, text=txt)


async def notify_own(cli: Client, txt: str):
    o_ids = Var.OWNER_ID if isinstance(Var.OWNER_ID, (list, tuple, set)) else [Var.OWNER_ID]
    
    async def send_with_flood_wait(chat_id: int):
        try:
            await cli.send_message(chat_id=chat_id, text=txt)
        except FloodWait as e:
            await asyncio.sleep(e.value)
            await cli.send_message(chat_id=chat_id, text=txt)
    
    tasks = [send_with_flood_wait(oid) for oid in o_ids]
    if hasattr(Var, 'BIN_CHANNEL') and isinstance(Var.BIN_CHANNEL, int) and Var.BIN_CHANNEL != 0:
        tasks.append(send_with_flood_wait(Var.BIN_CHANNEL))
    await asyncio.gather(*tasks, return_exceptions=True)


async def reply_user_err(msg: Message, err_txt: str):
    try:
        await msg.reply_text(
            text=err_txt,
            reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(MSG_BUTTON_GET_HELP, callback_data="help_command")]]),
            disable_web_page_preview=True
        )
    except FloodWait as e:
        await asyncio.sleep(e.value)
        await msg.reply_text(
            text=err_txt,
            reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(MSG_BUTTON_GET_HELP, callback_data="help_command")]]),
            disable_web_page_preview=True
        )


async def log_newusr(cli: Client, uid: int, fname: str):
    try:
        is_new = await db.add_user(uid)
        if not is_new:
            return
        if hasattr(Var, 'BIN_CHANNEL') and isinstance(Var.BIN_CHANNEL, int) and Var.BIN_CHANNEL != 0:
            try:
                await cli.send_message(chat_id=Var.BIN_CHANNEL, text=MSG_NEW_USER.format(first_name=fname, user_id=uid))
            except FloodWait as e:
                await asyncio.sleep(e.value)
                await cli.send_message(chat_id=Var.BIN_CHANNEL, text=MSG_NEW_USER.format(first_name=fname, user_id=uid))
    except Exception as e:
        logger.error(f"Database error in log_newusr for user {uid}: {e}")


async def gen_links(fwd_msg: Message, shortener: bool = True) -> Dict[str, str]:
    fid = fwd_msg.id
    m_name_raw = get_fname(fwd_msg)
    m_name = m_name_raw.decode('utf-8', errors='replace') if isinstance(m_name_raw, bytes) else str(m_name_raw)
    m_size_hr = humanbytes(get_fsize(fwd_msg))
    enc_fname = quote_media_name(m_name)
    f_hash = get_hash(fwd_msg)
    return await _build_links(
        download_path=f"/{f_hash}{fid}/{enc_fname}",
        stream_path=f"/watch/{f_hash}{fid}/{enc_fname}",
        media_name=m_name,
        media_size=m_size_hr,
        shortener=shortener
    )


async def gen_dc_txt(usr: User) -> str:
    dc_id_val = usr.dc_id if usr.dc_id is not None else MSG_DC_UNKNOWN
    return MSG_DC_USER_INFO.format(user_name=usr.first_name or 'User', user_id=usr.id, dc_id=dc_id_val)


async def get_user(cli: Client, qry: Any) -> Optional[User]:
    if isinstance(qry, str):
        if qry.startswith('@'):
            try:
                return await cli.get_users(qry)
            except FloodWait as e:
                await asyncio.sleep(e.value)
                return await cli.get_users(qry)
        elif qry.isdigit():
            try:
                return await cli.get_users(int(qry))
            except FloodWait as e:
                await asyncio.sleep(e.value)
                return await cli.get_users(int(qry))
    elif isinstance(qry, int):
        try:
            return await cli.get_users(qry)
        except FloodWait as e:
            await asyncio.sleep(e.value)
            return await cli.get_users(qry)
    return None


async def is_admin(cli: Client, chat_id_val: int) -> bool:
    try:
        member = await cli.get_chat_member(chat_id_val, cli.me.id)
    except FloodWait as e:
        await asyncio.sleep(e.value)
        try:
            member = await cli.get_chat_member(chat_id_val, cli.me.id)
        except Exception:
            return False
    except Exception:
        return False
    if member is None:
        return False
    return member.status in [ChatMemberStatus.ADMINISTRATOR, ChatMemberStatus.OWNER]


async def reply(msg: Message, **kwargs):
    try:
        return await msg.reply_text(**kwargs, quote=True, disable_web_page_preview=True)
    except FloodWait as e:
        await asyncio.sleep(e.value)
        return await msg.reply_text(**kwargs, quote=True, disable_web_page_preview=True)