diff options
author | hyfc <[email protected]> | 2018-06-10 23:27:45 +0800 |
---|---|---|
committer | hyfc <[email protected]> | 2020-10-22 11:37:35 +0800 |
commit | 9d71b348da48ac3c9c5da2e36c8741a7bd38ef1a (patch) | |
tree | 4374148dbd08af11e87642db928a42d46e88b961 | |
parent | 0d46d715374b51bd4a0833e7687ae33ae46376b3 (diff) | |
download | telegram-mail-bot-9d71b348da48ac3c9c5da2e36c8741a7bd38ef1a.tar.gz telegram-mail-bot-9d71b348da48ac3c9c5da2e36c8741a7bd38ef1a.tar.bz2 telegram-mail-bot-9d71b348da48ac3c9c5da2e36c8741a7bd38ef1a.zip |
a first working verison
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | Pipfile | 13 | ||||
-rw-r--r-- | Pipfile.lock | 50 | ||||
-rw-r--r-- | Procfile | 1 | ||||
-rw-r--r-- | bot.py | 101 | ||||
-rw-r--r-- | utils/mail.py | 128 |
6 files changed, 287 insertions, 12 deletions
@@ -102,3 +102,9 @@ venv.bak/ # mypy .mypy_cache/ + +#IDEA files +.idea/ + +#vscode files +.vscode/
\ No newline at end of file @@ -0,0 +1,13 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +pytz = "*" +python-telegram-bot = "*" + +[dev-packages] + +[requires] +python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..49e9354 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,50 @@ +{ + "_meta": { + "hash": { + "sha256": "f7da4ab6099df3aff82e024a8a850f8e6606a3ee0ecdbbc55e20e22d40864c5e" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.6" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "certifi": { + "hashes": [ + "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7", + "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0" + ], + "version": "==2018.4.16" + }, + "future": { + "hashes": [ + "sha256:e39ced1ab767b5936646cedba8bcce582398233d6a627067d4c6a454c90cfedb" + ], + "version": "==0.16.0" + }, + "python-telegram-bot": { + "hashes": [ + "sha256:725a28f77a04d056559015963a21eacf5d2d1f1722192237284828b7cc437465", + "sha256:ca2f8a44ddef7271477e16f4986647fa90ef4df5b55a7953e53b9c9d2672f639" + ], + "index": "pypi", + "version": "==10.1.0" + }, + "pytz": { + "hashes": [ + "sha256:65ae0c8101309c45772196b21b74c46b2e5d11b6275c45d251b150d5da334555", + "sha256:c06425302f2cf668f1bba7a0a03f3c1d34d4ebeef2c72003da308b3947c7f749" + ], + "index": "pypi", + "version": "==2018.4" + } + }, + "develop": {} +} diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..c22255a --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +bot: python3 bot.py
\ No newline at end of file @@ -0,0 +1,101 @@ +import logging +import os + +from utils.mail import EmailClient +from telegram import ParseMode +from telegram.constants import MAX_MESSAGE_LENGTH +from telegram.ext import (Updater, CommandHandler) + + +logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + level=logging.INFO) +logger = logging.getLogger(__name__) + +bot_token = os.environ['TELEGRAM_TOKEN'] + + +def error(bot, update, _error): + """Log Errors caused by Updates.""" + logger.warning('Update "%s" caused error "%s"', update, _error) + +def start_callback(bot, update): + msg = "Use /help to get help" + update.message.reply_text(msg) + +def _help(bot, update): + """Send a message when the command /help is issued.""" + help_str = "*Mailbox Setting*: \n" \ + "/setting [email protected] yourpassword" + bot.send_message(update.message.chat_id, + parse_mode=ParseMode.MARKDOWN, + text=help_str) + +def setting_email(bot, update, args): + global email_addr, email_passwd + email_addr = args[0] + email_passwd = args[1] + + update.message.reply_text("Configure email success!") + +def inbox(bot, update): + with EmailClient(email_addr, email_passwd) as client: + num_of_mails = client.get_mails_count() + reply_text = "The index of newest mail is *%d*" % num_of_mails + bot.send_message(update.message.chat_id, + parse_mode=ParseMode.MARKDOWN, + text=reply_text) + +def get_email(bot, update, args): + index = args[0] + with EmailClient(email_addr, email_passwd) as client: + mail = client.get_mail_by_index(index) + subject = "*Subject*: %s\n" % mail.subject + sender = "*From*: %s - %s\n" % (mail.from_nickname, mail.from_account) + date = "*Date*: %s\n" % mail.receivedtime + bot.send_message(update.message.chat_id, + parse_mode=ParseMode.MARKDOWN, + text=subject+sender+date) + if len(mail.text_content) > MAX_MESSAGE_LENGTH: + text = mail.text_content[0:4096] + bot.send_message(update.message.chat_id, + text=text) + mail.text_content = mail.text_content.lstrip(text) + if mail.text_content: + bot.send_message(update.message.chat_id, + text=mail.text_content) + + + +def main(): + # Create the EventHandler and pass it your bot's token. + updater = Updater(token=bot_token) + + # Get the dispatcher to register handlers + dp = updater.dispatcher + + # simple start function + dp.add_handler(CommandHandler("start", start_callback)) + + dp.add_handler(CommandHandler("help", _help)) + # + # Add command handler to set email address and account. + dp.add_handler(CommandHandler("setting", setting_email, pass_args=True)) + + dp.add_handler(CommandHandler("inbox", inbox)) + + dp.add_handler(CommandHandler("get", get_email, pass_args=True)) + + + dp.add_error_handler(error) + + # Start the Bot + updater.start_polling() + + # Run the bot until you press Ctrl-C or the process receives SIGINT, + # SIGTERM or SIGABRT. This should be used most of the time, since + # start_polling() is non-blocking and will stop the bot gracefully. + updater.idle() + + +if __name__ == '__main__': + main()
\ No newline at end of file diff --git a/utils/mail.py b/utils/mail.py index 3dc346f..f26e7b8 100644 --- a/utils/mail.py +++ b/utils/mail.py @@ -1,20 +1,97 @@ +import base64 +from datetime import datetime +from email.parser import Parser +import logging import poplib +import pytz +import re -class MailInfo(object): - """ - Class for storing mail's metadata - """ +logger = logging.getLogger(__name__) + +class MailDetails(object): def __init__(self): - self.index = 0 - self.size = 0 - self.status = "" - self.data = "" + self.from_nickname = "" + self.from_account = "" + self.to_nickname = "" + self.to_account = "" + self.subject = "" + self.receivedtime = "" + self.text_content = "" + self.html_content = "" + + +def decode_byte(bstr, charset='utf8'): + return bstr.decode(charset) + +def get_rawcontent_charset(rawcontent): + for item in rawcontent: + if decode_byte(item).find('charset='): + charset = re.findall(re.compile('charset="(.*)"'), decode_byte(item)) + for member in charset: + if member is not None: + return member + + +def parse_raw_mail_data(raw_lines, charset='utf8'): + msg_content = b'\r\n'.join(raw_lines).decode(encoding=charset) + return Parser().parsestr(text=msg_content) + +def decode_base64(s, charset='utf8'): + return str(base64.decodebytes(s.encode(encoding=charset)), encoding=charset) + + +def get_mail_info(s): + try: + nickname, account = s.split(" ") + except ValueError: + nickname = '' + account = s + + account = account.lstrip('<') + account = account.rstrip('>') + return nickname, account + +def get_mail_details(msg): + maildetails = MailDetails() + + fromstr = msg.get('From') + from_nickname, from_account = get_mail_info(fromstr) + maildetails.from_nickname = from_nickname + maildetails.from_account = from_account + tostr = msg.get('To') + to_nickname, to_account = get_mail_info(tostr) + maildetails.to_nickname = to_nickname + maildetails.to_account = to_account + + subject = msg.get('Subject') + try: + maildetails.subject = decode_base64(subject.split("?")[3], charset=subject.split("?")[1]) + except IndexError: + maildetails.subject = subject + received_time = msg.get("Date") + time_str_fmt = "%a, %d %b %Y %H:%M:%S %z" + time_obj = datetime.strptime(received_time, time_str_fmt) + time_obj = time_obj.astimezone(pytz.timezone('Asia/Hong_Kong')) + maildetails.receivedtime = time_obj.strftime(time_str_fmt) + + parts = msg.get_payload() + content_charset = parts[0].get_content_charset() + content = parts[0].as_string().split('base64')[-1] + try: + maildetails.text_content = decode_base64(content, content_charset) + except Exception as e: + logger.error('Exception caught: "%s"', e) + maildetails.text_content = content + content = parts[1].as_string().split('base64')[-1] + maildetails.html_content = content + + return maildetails class EmailClient(object): - def __init__(self, email_account, password): + def __init__(self, email_account, passwd): self.email_account = email_account - self.password = password + self.password = passwd self.server = self.connect(self) @staticmethod @@ -34,15 +111,42 @@ class EmailClient(object): return server - def get_mail_count(self): + def get_mails_list(self): _, mails, _ = self.server.list() + return mails + + def get_mails_count(self): + mails = self.get_mails_list() return len(mails) + def get_mail_by_index(self, index): + resp_status, mail_lines, mail_octets = self.server.retr(index) + content_charset = get_rawcontent_charset(mail_lines) + data = parse_raw_mail_data(mail_lines, charset=content_charset or 'utf-8') + maildetails = get_mail_details(data) + return maildetails + + + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type is None: + print('exited normally\n') + self.server.quit() + else: + print('raise an exception! ' + str(exc_type)) + self.server.close() + return False # Propagate + + + if __name__ == '__main__': useraccount = "XXXXX" password = "XXXXXX" client = EmailClient(useraccount, password) - client.get_mail_count()
\ No newline at end of file + client.get_mails_count()
\ No newline at end of file |