mirror of
https://github.com/Xtao-Labs/mail2telegram.git
synced 2024-11-27 01:33:13 +00:00
init
This commit is contained in:
parent
94cfe7178f
commit
a1e2b4ea12
4
.gitignore
vendored
4
.gitignore
vendored
@ -157,4 +157,6 @@ cython_debug/
|
|||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
.idea/
|
||||||
|
config.ini
|
||||||
|
*.session*
|
||||||
|
13
config.gen.ini
Normal file
13
config.gen.ini
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[pyrogram]
|
||||||
|
api_id = 143461
|
||||||
|
api_hash = 7b8a66cb31224f4241102d7fc57b5bcd
|
||||||
|
|
||||||
|
[basic]
|
||||||
|
ipv6 = False
|
||||||
|
cache_uri = mem://
|
||||||
|
|
||||||
|
[imap]
|
||||||
|
host = xxx
|
||||||
|
username = xxx
|
||||||
|
password = xxx
|
||||||
|
chat_id = 1111
|
36
defs/models.py
Normal file
36
defs/models.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pyrogram import Client
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
TEMP = """#mail
|
||||||
|
✉️ %s (%s)
|
||||||
|
To: <code>%s</code>
|
||||||
|
|
||||||
|
%s"""
|
||||||
|
|
||||||
|
|
||||||
|
class Mail(BaseModel):
|
||||||
|
id: int
|
||||||
|
from_: str
|
||||||
|
subject: str
|
||||||
|
to: str
|
||||||
|
|
||||||
|
@property
|
||||||
|
def from_name(self) -> str:
|
||||||
|
li = self.from_.split(" <")
|
||||||
|
return " <".join(li[:-1]).strip()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def from_at(self) -> str:
|
||||||
|
li = self.from_.split(" <")
|
||||||
|
return li[-1].strip()[:-1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def text(self) -> str:
|
||||||
|
return TEMP % (self.from_name, self.from_at, self.to, self.subject)
|
||||||
|
|
||||||
|
async def send(self, bot: "Client", chat_id: int) -> "Message":
|
||||||
|
return await bot.send_message(chat_id, self.text)
|
54
defs/search.py
Normal file
54
defs/search.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import email.header
|
||||||
|
from datetime import date, datetime, timedelta
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from cashews import cache
|
||||||
|
from imapclient import IMAPClient
|
||||||
|
|
||||||
|
from defs.models import Mail
|
||||||
|
from glover import host, days, username, password
|
||||||
|
|
||||||
|
|
||||||
|
def decode_mime_words(s):
|
||||||
|
return u''.join(
|
||||||
|
word.decode(encoding or 'utf8') if isinstance(word, bytes) else word
|
||||||
|
for word, encoding in email.header.decode_header(s))
|
||||||
|
|
||||||
|
|
||||||
|
def get_date() -> date:
|
||||||
|
now = datetime.now()
|
||||||
|
old = now - timedelta(days=days)
|
||||||
|
return date(old.year, old.month, old.day)
|
||||||
|
|
||||||
|
|
||||||
|
async def filter_mail(ids: List[int]) -> List[int]:
|
||||||
|
new = []
|
||||||
|
for mid in ids:
|
||||||
|
if await cache.get(f"mail:{username}:{mid}"):
|
||||||
|
continue
|
||||||
|
new.append(mid)
|
||||||
|
return new
|
||||||
|
|
||||||
|
|
||||||
|
async def search() -> List[Mail]:
|
||||||
|
with IMAPClient(host=host) as client:
|
||||||
|
client.login(username, password)
|
||||||
|
client.select_folder("INBOX")
|
||||||
|
|
||||||
|
messages = client.search([u'SINCE', get_date()])
|
||||||
|
messages = await filter_mail(messages)
|
||||||
|
response = client.fetch(messages, ["RFC822"])
|
||||||
|
mails = []
|
||||||
|
for message_id, data in response.items():
|
||||||
|
email_message = email.message_from_bytes(data[b"RFC822"])
|
||||||
|
from_ = email_message.get("From")
|
||||||
|
subject = decode_mime_words(email_message.get("Subject"))
|
||||||
|
to = email_message.get("To")
|
||||||
|
mail = Mail(
|
||||||
|
id=message_id,
|
||||||
|
from_=from_,
|
||||||
|
subject=subject,
|
||||||
|
to=to,
|
||||||
|
)
|
||||||
|
mails.append(mail)
|
||||||
|
return mails
|
47
glover.py
Normal file
47
glover.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
from typing import Union
|
||||||
|
from configparser import RawConfigParser
|
||||||
|
|
||||||
|
|
||||||
|
def strtobool(val, default=False):
|
||||||
|
"""Convert a string representation of truth to true (1) or false (0).
|
||||||
|
|
||||||
|
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
|
||||||
|
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
|
||||||
|
'val' is anything else.
|
||||||
|
"""
|
||||||
|
if val is None:
|
||||||
|
return default
|
||||||
|
val = val.lower()
|
||||||
|
if val in ("y", "yes", "t", "true", "on", "1"):
|
||||||
|
return 1
|
||||||
|
elif val in ("n", "no", "f", "false", "off", "0"):
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
print("[Degrade] invalid truth value %r" % (val,))
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
# [pyrogram]
|
||||||
|
api_id: int = 0
|
||||||
|
api_hash: str = ""
|
||||||
|
# [Basic]
|
||||||
|
ipv6: Union[bool, str] = "False"
|
||||||
|
cache_uri: str = "mem://"
|
||||||
|
# [Imap]
|
||||||
|
host: str = ""
|
||||||
|
username: str = ""
|
||||||
|
password: str = ""
|
||||||
|
days: int = 3
|
||||||
|
chat_id: int = 0
|
||||||
|
|
||||||
|
config = RawConfigParser()
|
||||||
|
config.read("config.ini")
|
||||||
|
api_id = config.getint("pyrogram", "api_id", fallback=api_id)
|
||||||
|
api_hash = config.get("pyrogram", "api_hash", fallback=api_hash)
|
||||||
|
ipv6 = strtobool(config.get("basic", "ipv6", fallback=ipv6))
|
||||||
|
cache_uri = config.get("basic", "cache_uri", fallback=cache_uri)
|
||||||
|
host = config.get("imap", "host", fallback=host)
|
||||||
|
username = config.get("imap", "username", fallback=username)
|
||||||
|
password = config.get("imap", "password", fallback=password)
|
||||||
|
days = config.getint("imap", "days", fallback=days)
|
||||||
|
chat_id = config.getint("imap", "chat_id", fallback=chat_id)
|
33
init.py
Normal file
33
init.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import pyrogram
|
||||||
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
from cashews import cache
|
||||||
|
|
||||||
|
from glover import api_id, api_hash, ipv6, cache_uri
|
||||||
|
from logging import getLogger, INFO, StreamHandler, basicConfig, CRITICAL, Formatter
|
||||||
|
|
||||||
|
# Set Cache
|
||||||
|
cache.setup(cache_uri)
|
||||||
|
# Enable logging
|
||||||
|
logs = getLogger(__name__)
|
||||||
|
logging_handler = StreamHandler()
|
||||||
|
dt_fmt = "%Y-%m-%d %H:%M:%S"
|
||||||
|
formatter = Formatter(
|
||||||
|
"[{asctime}] [{levelname:<8}] {name}: {message}", dt_fmt, style="{"
|
||||||
|
)
|
||||||
|
logging_handler.setFormatter(formatter)
|
||||||
|
root_logger = getLogger()
|
||||||
|
root_logger.setLevel(CRITICAL)
|
||||||
|
root_logger.addHandler(logging_handler)
|
||||||
|
pyro_logger = getLogger("pyrogram")
|
||||||
|
pyro_logger.setLevel(CRITICAL)
|
||||||
|
pyro_logger.addHandler(logging_handler)
|
||||||
|
basicConfig(level=INFO)
|
||||||
|
logs.setLevel(INFO)
|
||||||
|
|
||||||
|
scheduler = AsyncIOScheduler(timezone="Asia/ShangHai")
|
||||||
|
if not scheduler.running:
|
||||||
|
scheduler.start()
|
||||||
|
# Init client
|
||||||
|
bot = pyrogram.Client(
|
||||||
|
"bot", api_id=api_id, api_hash=api_hash, ipv6=ipv6, plugins=dict(root="modules")
|
||||||
|
)
|
10
main.py
Normal file
10
main.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from pyrogram import idle
|
||||||
|
|
||||||
|
from init import bot, logs
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logs.info("Bot 开始运行")
|
||||||
|
bot.start()
|
||||||
|
logs.info(f"Bot 启动成功!@{bot.me.username}")
|
||||||
|
idle()
|
||||||
|
bot.stop()
|
7
modules/ping.py
Normal file
7
modules/ping.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from pyrogram import Client, filters
|
||||||
|
from pyrogram.types import Message
|
||||||
|
|
||||||
|
|
||||||
|
@Client.on_message(filters.incoming & filters.private & filters.command(["ping"]))
|
||||||
|
async def ping_command(_: Client, message: Message):
|
||||||
|
await message.reply("pong~", quote=True)
|
22
modules/update.py
Normal file
22
modules/update.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from cashews import cache
|
||||||
|
|
||||||
|
from glover import chat_id, username
|
||||||
|
from init import scheduler, bot, logs
|
||||||
|
from defs.search import search
|
||||||
|
|
||||||
|
|
||||||
|
# 15 分钟执行一次
|
||||||
|
@scheduler.scheduled_job("interval", minutes=15)
|
||||||
|
async def update():
|
||||||
|
logs.info("开始检查新邮件")
|
||||||
|
mails = await search()
|
||||||
|
for m in mails:
|
||||||
|
try:
|
||||||
|
await m.send(bot, chat_id)
|
||||||
|
await cache.set(f"mail:{username}:{m.id}", 1)
|
||||||
|
except Exception as e:
|
||||||
|
logs.exception("发送邮件失败", exc_info=e)
|
||||||
|
logs.info("检查新邮件结束")
|
||||||
|
|
||||||
|
|
||||||
|
bot.loop.create_task(update())
|
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
imapclient
|
||||||
|
apscheduler
|
||||||
|
pyrogram
|
||||||
|
tgcrypto
|
||||||
|
pydantic
|
||||||
|
cashews[redis]
|
Loading…
Reference in New Issue
Block a user