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
|
||||
# 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.
|
||||
#.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