├── 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 |
8 | 9 | Telegram 10 | 11 |
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 | ![image](https://github.com/Delafault/Telegram-copy-paste-bot/assets/78411508/db512e07-4b3f-4ce1-88c9-92d580eb058b) 45 | --- 46 | 2. Use the command "`pip install -r requirements`" or just run the "`install.bat`" file (recommended) 47 | ![image](https://github.com/Delafault/Telegram-copy-paste-bot/assets/78411508/9656bff1-c935-4362-9c59-c8fd8452028c) 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 | ![image](https://github.com/Delafault/Telegram-copy-paste-bot/assets/78411508/d8603d6f-4e76-4f6c-8878-a9f114602355) 51 | --- 52 | 4. Done! Run the `start.bat` file to start the script. Good luck! 53 | ![image](https://github.com/Delafault/Telegram-copy-paste-bot/assets/78411508/7122caf1-ab95-4978-9fde-d0bc764f64cf) 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 | --------------------------------------------------------------------------------