Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add sending message option and command to anyone for better experience and automation flow #269

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ downloaded until the last text message.

$ telegram-download

You can also **send messages** only to your saved messages or to a specific chat:
.. code-block:: console

$ telegram-message "Hello, world!"

`Read the documentation <https://docs.nekmo.org/telegram-upload/usage.html#telegram-download>`_ for more info about the
options availables.

Expand All @@ -85,6 +90,7 @@ a **terminal 🪄 wizard**. It even **supports mouse**!

$ telegram-upload --interactive # Interactive upload
$ telegram-download --interactive # Interactive download
$ telegram-message --interactive # Interactive message

`More info in the documentation <https://docs.nekmo.org/telegram-upload/usage.html#interactive-mode>`_

Expand Down
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
MODULE = 'telegram_upload'
REQUIREMENT_FILE = 'requirements.txt'
STATUS_LEVEL = 5 # 1:Planning 2:Pre-Alpha 3:Alpha 4:Beta 5:Production/Stable 6:Mature 7:Inactive
KEYWORDS = ['telegram-upload', 'telegram', 'upload', 'video']
KEYWORDS = ['telegram-upload', 'telegram', 'upload', 'video', 'messenger', 'file', 'bot']
LICENSE = 'MIT license'

CLASSIFIERS = [ # https://github.com/github/choosealicense.com/tree/gh-pages/_licenses
Expand All @@ -40,7 +40,7 @@
# 'ios'
# 'android'
]
PYTHON_VERSIONS = ['3.7-3.9', '3.10', '3.11']
PYTHON_VERSIONS = ['3.7-3.9', '3.10', '3.11', '3.12']


def read_requirement_file(path):
Expand Down Expand Up @@ -140,6 +140,7 @@ def get_platform_classifiers(platform):
"console_scripts": [
"telegram-upload = telegram_upload.management:upload_cli",
"telegram-download = telegram_upload.management:download_cli",
"telegram-message = telegram_upload.management:message_cli",
],
},

Expand Down
3 changes: 2 additions & 1 deletion telegram_upload/client/telegram_manager_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from telegram_upload.client.telegram_download_client import TelegramDownloadClient
from telegram_upload.client.telegram_upload_client import TelegramUploadClient
from telegram_upload.client.telegram_message_client import TelegramMessageClient
from telegram_upload.config import SESSION_FILE
from telegram_upload.exceptions import TelegramProxyError, InvalidApiFileError

Expand Down Expand Up @@ -82,7 +83,7 @@ def parse_proxy_string(proxy: Union[str, None]):
proxy_parsed.username, proxy_parsed.password)


class TelegramManagerClient(TelegramUploadClient, TelegramDownloadClient):
class TelegramManagerClient(TelegramUploadClient, TelegramDownloadClient, TelegramMessageClient):
def __init__(self, config_file, proxy=None, **kwargs):
with open(config_file) as f:
config = json.load(f)
Expand Down
53 changes: 53 additions & 0 deletions telegram_upload/client/telegram_message_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import time
from typing import Iterable, Optional

import click
from telethon import TelegramClient
from telethon.errors import RPCError, FloodWaitError, InvalidBufferError
from enum import Enum

RETRIES = 3

class ParsMode(str, Enum):
HTML = 'html'
MARKDOWN = 'markdown'
MD = 'md'


class TelegramMessageClient(TelegramClient):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def forward_to(self, message, destinations):
for destination in destinations:
self.forward_messages(destination, [message])

def send_one_message(self, entity, text_message: str, retries=RETRIES, parse_mode: Optional[ParsMode] = None):
message = None
try:
message = self.send_message(entity, text_message, parse_mode=parse_mode)
except FloodWaitError as e:
click.echo(f'{e}. Waiting for {e.seconds} seconds.', err=True)
time.sleep(e.seconds)
message = self.send_one_message(entity, text_message, retries=retries, parse_mode=parse_mode)
except RPCError as e:
if retries > 0:
click.echo(f'The message "{text_message}" could not be sent: {e}. Retrying...', err=True)
message = self.send_one_message(entity, text_message, retries=retries - 1, parse_mode=parse_mode)
else:
click.echo(f'The message "{text_message}" could not be sent: {e}. It will not be retried.', err=True)
return message

def send_messages(self, entity, text_messages: Iterable[str], parse_mode: Optional[ParsMode] = None, forward=()):
messages = []
for text_msg in text_messages:
try:
message = self.send_one_message(entity, text_msg, parse_mode = parse_mode)
finally:
pass
if message is None:
click.echo('Failed to send message "{}"'.format(text_msg), err=True)
if message:
self.forward_to(message, forward)
messages.append(message)
return messages
43 changes: 42 additions & 1 deletion telegram_upload/management.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,15 +231,56 @@ def download(from_, config, delete_on_success, proxy, split_files, interactive):
client.download_files(from_, download_files, delete_on_success)


@click.command()
@click.argument('messages', nargs=-1)
@click.option('--to', default=None, help='Phone number, username, invite link or "me" (saved messages). '
'By default "me".')
@click.option('--config', default=None, help='Configuration file to use. By default "{}".'.format(CONFIG_FILE))
@click.option('-pm', '--parse_mode', default=None,
help='Parse mode for the message. Options: "text", "markdown", "html". By default "text" or None.')
@click.option('-f', '--forward', multiple=True, help='Forward the file to a chat (alias or id) or user (username, '
'mobile or id). This option can be used multiple times.')
@click.option('-p', '--proxy', default=None,
help='Use an http proxy, socks4, socks5 or mtproxy. For example socks5://user:pass@1.2.3.4:8080 '
'for socks5 and mtproxy://secret@1.2.3.4:443 for mtproxy.')
@click.option('-i', '--interactive', is_flag=True,
help='Use interactive mode.')
def message(messages, to, config, parse_mode, proxy, interactive, forward):
"""Send messages to a chat or user."""
client = TelegramManagerClient(config or default_config(), proxy=proxy)
client.start()
if not messages:
click.echo('No messages to send. Should be at least one message within quotation marks.')
return
if parse_mode:
parse_mode = parse_mode.lower()
if parse_mode not in ('text', 'markdown', 'html'):
click.echo('Invalid parse mode. Options: "text", "markdown", "html".')
parse_mode = None
if parse_mode == 'text':
parse_mode = None
else:
parse_mode = None
if interactive and not to:
click.echo('Select the recipient dialog of the messages:')
click.echo('[SPACE] Select dialog [ENTER] Next step')
to = async_to_sync(interactive_select_dialog(client))
elif to is None:
to = 'me'
if isinstance(to, str) and to.lstrip("-+").isdigit():
to = int(to)
client.send_messages(to, messages, parse_mode, forward)

upload_cli = catch(upload)
download_cli = catch(download)
message_cli = catch(message)


if __name__ == '__main__':
import sys
import re
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
commands = {'upload': upload_cli, 'download': download_cli}
commands = {'upload': upload_cli, 'download': download_cli, 'message': message_cli}
if len(sys.argv) < 2:
sys.stderr.write('A command is required. Available commands: {}\n'.format(
', '.join(commands)
Expand Down