├── requirements.txt
├── start.bat
├── install.bat
├── config.py
├── LICENSE
├── README.md
└── main.py
/requirements.txt:
--------------------------------------------------------------------------------
1 | Telethon==1.34.0
2 | TgCrypto==1.2.5
3 | cryptg==0.4.0
--------------------------------------------------------------------------------
/start.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | chcp 65001 > nul
3 | echo Запускаю скрипт...
4 | timeout /t 3 >nul
5 | python main.py
6 | pause
--------------------------------------------------------------------------------
/install.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | chcp 65001 > nul
3 |
4 | pip show telethon > nul
5 | if errorlevel 1 (
6 | pip install telethon
7 | )
8 |
9 | pip show tgcrypto > nul
10 | if errorlevel 1 (
11 | pip install tgcrypto
12 | )
13 |
14 | pip show cryptg > nul
15 | if errorlevel 1 (
16 | pip install cryptg
17 | )
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | # Description: Конфигурация для юзербота.
2 | API_ID = 123 # API_ID и API_HASH можно получить на my.telegram.org
3 | API_HASH = "asd123" # ↑
4 | PHONE_NUMBER = "+0..." # Номер телефона для юзербота.
5 | DEVICE_MODEL = "Pixel 3 XL" # Модель устройства (можно не менять)
6 | SYSTEM_VERSION = "Android 10.0" # Версия системы (можно не менять)
7 |
8 | # Каналы для копирования и вставки
9 | CHANNELS_COPY = "@channel", "@conversation" # Каналы для копирования постов (через запятую)
10 | CHANNEL_PASTE = "@pastechannel" # Канал для вставки постов
11 | AUTO_DELETE_LINKS = False # Удаление ссылок в описании (True - удалять ссылку вместе с её текстом, если таковая имеется, False - ничего не делать со ссылками, None - удалять только ссылку, оставляя её текст, str - заменить ссылку на указанную ссылку(например, "AUTO_DELETE_LINKS = "https://t.me/your_channel""))
12 | FORWARDS = None # True - обрабатывать только пересланные сообщения, False - не обрабатывать пересланные сообщения, None - обрабатывать все сообщения.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 DeFault
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Copy-past posts userbot for Telegram
4 |
5 |
6 |
7 |
12 |
13 | Script for copying messages from one channel and pasting them into other channels (with the help of a userbot).
14 |
15 |
16 | ---
17 |
18 | Support:
19 |
20 | - ✅ Copying from any channel (including those that prohibit copying messages)
21 | - ✅ Any text and any formatting (except spoilers)
22 | - ✅ Any types of files and other things (mugs, voice messages, videos, .other files)
23 | - ✅ Ability to remove links from received posts/change links to your own or leave only the link text without the link.
24 | - ✅ Spoilers for photos not included in the album.
25 | - ✅ Tracking an unlimited number of channels.
26 | - ✅ Everything else.
27 |
28 | Not support:
29 |
30 | - ❌ Inline buttons (because this is only available to bots, and the script uses a userbot)
31 | - ❌ spoilers for text (html text markup just doesn't show if spoiler is used in the text) and album (Takes too long to process)
32 |
33 | (If you find any bugs, flaws or have any idea for this script, please let me know, e.g. in issues)
34 |
35 |
36 |
37 | -- How use? --
38 |
39 |
40 |
41 | ---
42 |
43 | 1. Download this repository and python on [python.org](https://python.org)
44 | 
45 | ---
46 | 2. Use the command "`pip install -r requirements`" or just run the "`install.bat`" file (recommended)
47 | 
48 | ---
49 | 3. Enter all necessary data (including `api_id` and `api_hash`, which can be obtained from [my.telegram.org](https://my.telegram.org/auth)) in the 'config.py' file.
50 | 
51 | ---
52 | 4. Done! Run the `start.bat` file to start the script. Good luck!
53 | 
54 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from telethon.errors import PhoneNumberBannedError, PasswordHashInvalidError, UsernameInvalidError
2 | from telethon.types import DocumentAttributeFilename, InputMediaUploadedPhoto
3 | from telethon.sync import TelegramClient, events
4 | import os
5 | import re
6 |
7 | from config import (API_ID,
8 | API_HASH,
9 | PHONE_NUMBER,
10 | CHANNELS_COPY,
11 | CHANNEL_PASTE,
12 | DEVICE_MODEL,
13 | SYSTEM_VERSION,
14 | AUTO_DELETE_LINKS,
15 | FORWARDS)
16 |
17 | logo = """
18 | █▀▀ █▀█ █▀█ █▄█ ▄▄ █▀█ ▄▀█ █▀ ▀█▀ █▀▀ █▄▄ █▀█ ▀█▀|ᵇʸ ᵈᵉˡᵃᶠᵃᵘˡᵗ
19 | █▄▄ █▄█ █▀▀ ░█░ ░░ █▀▀ █▀█ ▄█ ░█░ ██▄ █▄█ █▄█ ░█░"""
20 |
21 | last_id_message = []
22 |
23 | def gd_print(value):
24 | green_color = '\033[32m'
25 | reset_color = '\033[0m'
26 | result = f"\n>{green_color} {value} {reset_color}\n"
27 | print(result)
28 |
29 | def bd_print(value):
30 | red_color = '\033[31m'
31 | reset_color = '\033[0m'
32 | result = f"\n>{red_color} {value} {reset_color}\n"
33 | print(result)
34 |
35 | async def check_caption(caption):
36 | if AUTO_DELETE_LINKS is False:
37 | return caption
38 | elif AUTO_DELETE_LINKS is True:
39 | return re.sub(r']*>.*?', '', caption)
40 | elif AUTO_DELETE_LINKS is None:
41 | return re.sub(r']*>(.*?)', r'\1', caption)
42 | elif AUTO_DELETE_LINKS not in [True, False, None]:
43 | return re.sub(r']*?\s+)?href="([^"]*)"(?:[^>]*)>(.*?)', rf'\2', caption)
44 |
45 | client = TelegramClient(
46 | session = f"tg_{PHONE_NUMBER}",
47 | api_id = API_ID,
48 | api_hash = API_HASH,
49 | device_model = DEVICE_MODEL,
50 | system_version = SYSTEM_VERSION
51 | )
52 |
53 | @client.on(events.Album(CHANNELS_COPY))
54 | async def album_handler(event):
55 | """
56 | Обработка альбомов
57 | """
58 | if FORWARDS is True:
59 | if event.messages[0].fwd_from:
60 | pass
61 | else:
62 | return
63 | elif FORWARDS is False:
64 | if event.messages[0].fwd_from:
65 | return
66 |
67 | media = []
68 | caption = event.messages[0].text
69 | force_document = False
70 | id = event.messages[0].id
71 | if id in last_id_message:
72 | last_id_message.clear()
73 | return # Album почему-то иногда получает ивент дважды на одно сообщение.
74 | last_id_message.append(id)
75 |
76 | caption = await check_caption(caption)
77 |
78 | gd_print(f"Получили альбом из {len(event)} сообщений.")
79 |
80 | for group_message in event.messages: # Да, spoiler для альбомов в теории можно реализовать, однако сделать это не так просто, как кажется. Плюсом занимало бы много времени на обработку.
81 | if group_message.photo:
82 | media.append(await client.download_media(group_message, f"temp_pics/{group_message.id}.png"))
83 | elif group_message.video:
84 | media.append(await client.download_media(group_message, f"temp_pics/{group_message.id}.mp4"))
85 | elif group_message.document:
86 | file_name = next((x.file_name for x in group_message.document.attributes if isinstance(x, DocumentAttributeFilename)), None)
87 | media.append(await client.download_media(group_message, f"temp_pics/{file_name}"))
88 | force_document = True
89 | else:
90 | return bd_print("Неизвестный тип сообщения")
91 |
92 | await client.send_file(CHANNEL_PASTE, media, caption=caption, force_document=force_document)
93 | gd_print(f"Скопировали и успешно отправили альбом из {len(event)} сообщений.")
94 |
95 | for file in media:
96 | os.remove(file) # использование временной папки оказалось самым удобным способом.
97 |
98 |
99 | @client.on(events.NewMessage(CHANNELS_COPY, forwards=FORWARDS))
100 | async def message_handler(event):
101 | """
102 | Обработка сообщений
103 | """
104 | if event.message.grouped_id is not None:
105 | return
106 |
107 | id = event.message.id
108 | caption = event.message.text
109 | spoiler = False
110 | web_preview = False
111 |
112 | try:
113 | if event.message.media.__dict__['spoiler'] is True:
114 | spoiler = True
115 | except AttributeError:
116 | pass
117 | except KeyError:
118 | pass
119 |
120 | try:
121 | if event.message.media.webpage.__dict__['url'] is not None:
122 | web_preview = True
123 | except AttributeError:
124 | pass
125 | except KeyError:
126 | pass
127 |
128 | gd_print(f"Получили сообщение {id}.")
129 |
130 | caption = await check_caption(caption)
131 |
132 | if event.message.photo and not web_preview:
133 | await client.download_media(event.message, f"temp_pics/pics_{id}.png")
134 | await client.send_file(CHANNEL_PASTE, InputMediaUploadedPhoto(await client.upload_file(f"temp_pics/pics_{id}.png"), spoiler=spoiler), caption=caption)
135 | os.remove(f"temp_pics/pics_{id}.png")
136 |
137 | elif event.message.video:
138 | await client.download_media(event.message, f"temp_pics/pics_{id}.mp4")
139 | await client.send_file(CHANNEL_PASTE, f"temp_pics/pics_{id}.mp4", caption=caption, video_note=True) # video_note позволяет отправлять кружки в виде кружков, однако из-за этого иногда GIF может ошибочно отправляться как кружок. Используйте video_note = False на своё усмотрение.
140 | os.remove(f"temp_pics/pics_{id}.mp4")
141 |
142 | elif event.message.document:
143 | file_name = next((x.file_name for x in event.message.document.attributes if isinstance(x, DocumentAttributeFilename)), None)
144 | if event.message.document.mime_type == "audio/ogg":
145 | path = await client.download_media(event.message, f"temp_pics/{id}")
146 | await client.send_file(CHANNEL_PASTE, path, voice_note=True)
147 | os.remove(path)
148 | return
149 | await client.download_media(event.message, f"temp_pics/{file_name}")
150 | await client.send_file(CHANNEL_PASTE, f"temp_pics/{file_name}", caption=caption, force_document=True)
151 | os.remove(f"temp_pics/{file_name}")
152 |
153 | else:
154 | try:
155 | await client.send_message(CHANNEL_PASTE, caption)
156 | except Exception as e:
157 | bd_print(f"Ошибка при отправке сообщения: {e}")
158 | return
159 |
160 | gd_print(f"Скопировали и успешно отправили сообщение {id}.")
161 |
162 |
163 |
164 |
165 | if __name__ == "__main__":
166 | try:
167 | client.start(phone=PHONE_NUMBER)
168 | os.system('cls' if os.name == 'nt' else 'clear')
169 | print(logo)
170 | gd_print(f"Успешно вошли в аккаунт {PHONE_NUMBER}.")
171 | client.parse_mode = "html"
172 |
173 | client.run_until_disconnected()
174 | gd_print(f"Сессия {PHONE_NUMBER} завершена.")
175 | except PhoneNumberBannedError:
176 | bd_print(f"Аккаунт {PHONE_NUMBER} заблокирован.")
177 | except PasswordHashInvalidError:
178 | bd_print(f"Неверный пароль для аккаунта {PHONE_NUMBER}.")
179 | except UsernameInvalidError:
180 | pass
181 | except Exception as e:
182 | bd_print(f"Неизвестная ошибка: {e}")
183 |
--------------------------------------------------------------------------------