This commit is contained in:
xtaodada 2024-02-22 22:26:21 +08:00
parent 94cfe7178f
commit a1e2b4ea12
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
10 changed files with 231 additions and 1 deletions

4
.gitignore vendored
View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,6 @@
imapclient
apscheduler
pyrogram
tgcrypto
pydantic
cashews[redis]