From 7b04d8bc16b2cc02f131cc870ab27ca349e002e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=83=E8=83=9C=E5=8F=91?= Date: Tue, 13 Dec 2022 14:54:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9D=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + README.md | 38 +++++++++++++++ accessToken | 1 + app.py | 106 +++++++++++++++++++++++++++++++++++++++++ bgmTVApi.py | 61 ++++++++++++++++++++++++ config.yaml | 16 +++++++ generateMailContent.py | 17 +++++++ loadYaml.py | 9 ++++ mail.py | 46 ++++++++++++++++++ template/email1.html | 40 ++++++++++++++++ template/email2.html | 19 ++++++++ template/email3.html | 17 +++++++ 12 files changed, 371 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 accessToken create mode 100644 app.py create mode 100644 bgmTVApi.py create mode 100644 config.yaml create mode 100644 generateMailContent.py create mode 100644 loadYaml.py create mode 100644 mail.py create mode 100644 template/email1.html create mode 100644 template/email2.html create mode 100644 template/email3.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..48ffc97 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +### 说明 + +这个东西是在自建番剧影音服务器中由于 qBittorrent 的下载完成通知的模板过于简陋以及 JellyFin 的新增内容通知与下载完成的时机有一定偏差(模板有时候也不好看)而自学了两天 python 做出来用于给自己一个比较美观的邮件通知,并且可以搭配一些使用邮件触发的应用使用。 + +### 使用场景 + +这个脚本使用的 bgm.tv 的 api 用于获取番剧与剧集信息,如果想使用其他站点的 api,请自行修改。 + +### 使用方式 + +1. 将文件夹直接放到 qBittorrent 可以访问到的路径中 +2. 修改 config.yaml 文件中的配置 +3. 在 https://next.bgm.tv/demo/access-token 生成一个 Access Token, 并写入到 accessToken 文件中 +4. (可选)修改邮件模板和主题以及其他个性配置 +5. 确保 qBittorrent 的环境中有 python3 和 requests 包 +6. 在 qBittorrent 的下载完成时执行的输入框中填 `python3 <这里填路径>/bangumi-mail-notification/app.py "%N" "%D"` +7. 测试 + +### config.yaml 配置介绍 + +```yaml +email: + from: ['邮件来源显示名称', 'example@163.com'] # 发送方 + user: example@example.com # 发送邮件的账户 同发送方 + passwd: xxxxxxxxxxxxxx # 发送邮件的密码(非邮件账户密码) + smtp: smtp.163.com # smtp 邮件服务器 + port: 465 # 端口 ssl 为 True 时填 465 + ssl: True # 是否使用 ssl 加密 + # 配置收件人,同发送方 + to: [['收件人1名称', example1@163.com'], ['收件人2名称', example2@163.com']] + +# 需要进行剧集信息搜索的分类名称(qBittorrent的分类) +bangumiCategory: Bangumi + +# 媒体库的地址与图标url +mediaUrl: https://media.example.com +mediaIcon: https://picbed.example.com/media.png +``` diff --git a/accessToken b/accessToken new file mode 100644 index 0000000..cde986a --- /dev/null +++ b/accessToken @@ -0,0 +1 @@ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..ab4e2e8 --- /dev/null +++ b/app.py @@ -0,0 +1,106 @@ + +import sys +import re +from urllib.parse import quote +import bgmTVApi +import mail +from generateMailContent import generate +from loadYaml import loadYaml + +# 获取 qBittorrent 传递的参数 +name = sys.argv[1] +path = sys.argv[2] + +# 根据路径参数获取分类与番剧名称 +category = path.split('/')[2] +subject_name = quote(path.split('/')[3], 'utf-8') + +# 番剧信息 +subjectInfo = {} +isGetSubjectInfo = False + +# 剧集信息 +episodeInfo = {} +isGetEpisodeInfo = False + +# 错误信息 用于发生请求或者其他错误时,可以通过邮件告知到管理员 +errorMsgs = [] + +# 读取配置文件 +config = loadYaml('config.yaml') + +mediaUrl = config.get('mediaUrl') +mediaIcon = config.get('mediaIcon') + +# 是否是番剧分类,用于判断是否能够在bgmtv中获取信息 +isBangumi = category == config.get('bangumiCategory') + +# 从标题获取集号的规则 +rules = [ + r"(.*) - (\d{1,4}|\d{1,4}\.\d{1,2})(?:v\d{1,2})?(?: )?(?:END)?(.*)", + r"(.*)[\[ E](\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?: )?(?:END)?[\] ](.*)", + r"(.*)\[第(\d*\.*\d*)话(?:END)?\](.*)", + r"(.*)\[第(\d*\.*\d*)話(?:END)?\](.*)", + r"(.*)第(\d*\.*\d*)话(?:END)?(.*)", + r"(.*)第(\d*\.*\d*)話(?:END)?(.*)", +] + +if isBangumi: + # 获取集号 + episodeNo = 1 + for rule in rules: + match_obj = re.match(rule, name, re.I) + if match_obj is not None: + episodeNo = match_obj.group(2) + + # 获取番剧信息 + subjectRes = bgmTVApi.fetchSubjectInfo(subject_name) + subjectInfo = subjectRes['data'] + isGetSubjectInfo = subjectRes['status'] + if not isGetSubjectInfo: + errorMsgs.append(subjectRes['message']) + + # 获取剧集信息 + episodeRes = bgmTVApi.fetchEpisodeInfo(subjectInfo.get('id'), episodeNo) + episodeInfo = episodeRes['data'] + isGetEpisodeInfo = episodeRes['status'] + if not isGetEpisodeInfo: + errorMsgs.append(episodeRes['message']) + +# 确认邮件标题 +mailSubject = '' +if isGetSubjectInfo: + mailSubject = subjectInfo.get('name_cn') + ' 更新啦!' +else: + mailSubject = name + ' 下载完成' + +# 生成邮件内容 +mailContent = '' +if isGetSubjectInfo & isGetEpisodeInfo: + data = { + 'title': mailSubject, + 'name': episodeInfo.get('name_cn'), + 'image': subjectInfo.get('images').get('large'), + 'summary': subjectInfo.get('summary'), + 'desc': episodeInfo.get('desc'), + 'mediaUrl': mediaUrl, + 'mediaIcon': mediaIcon + } + mailContent = generate('./template/email1.html', data) +elif len(errorMsgs) > 0: + data = { + 'title': mailSubject, + 'name': name, + 'path': path, + 'errorMsgs': errorMsgs + } + mailContent = generate('./template/email2.html', data) +else: + data = { + 'title': mailSubject, + 'name': name, + 'path': path + } + mailContent = generate('./template/email3.html', data) + +mail.send(mailSubject, mailContent, isBangumi) diff --git a/bgmTVApi.py b/bgmTVApi.py new file mode 100644 index 0000000..90093dc --- /dev/null +++ b/bgmTVApi.py @@ -0,0 +1,61 @@ +import requests +from requests import exceptions + +# 消除ssl告警 +requests.packages.urllib3.disable_warnings() + +tokenFile = open('accessToken', encoding = "utf-8") +token = tokenFile.read() +tokenFile.close() + +headers = { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json', 'User-Agent': 'Hiiragi/bangumi-mail-notification' } + +def fetchSubjectInfo(subject_name): + result = { + 'status': False, + 'data': None, + 'message': '' + } + + try: + response = requests.get(url=f'https://api.bgm.tv/search/subject/{subject_name}?type=2&responseGroup=medium', verify=False, headers=headers) + except exceptions.Timeout as e: + result['message'] = '番剧信息请求超时:' + str(e.message) + except exceptions.HTTPError as e: + result['message'] = '番剧信息http请求错误:' + str(e.message) + else: + if response.status_code == 200: + resJson = response.json() + if resJson.get('list') is not None: + result['data'] = resJson.get('list')[0] + result['status'] = True + else: + result['message'] = '番剧信息请求错误:' + str(response.status_code) + ',' + str(response.reason) + + return result + +def fetchEpisodeInfo(subject_id, episodeNo): + result = { + 'status': False, + 'data': None, + 'message': '' + } + + try: + response = requests.get(url=f'https://api.bgm.tv/v0/episodes?subject_id={subject_id}', verify=False, headers=headers) + except exceptions.Timeout as e: + result['message'] = '剧集信息请求超时:' + str(e.message) + except exceptions.HTTPError as e: + result['message'] = '剧集信息http请求错误:' + str(e.message) + else: + if response.status_code == 200: + resJson = response.json() + if resJson.get('data') is not None: + for d in resJson.get('data'): + if d.get('ep') == int(episodeNo): + result['data'] = d + result['status'] = True + else: + result['message'] = '剧集信息请求错误:' + str(response.status_code) + ',' + str(response.reason) + + return result diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..f9042e6 --- /dev/null +++ b/config.yaml @@ -0,0 +1,16 @@ +email: + from: ['邮件来源显示名称', 'example@163.com'] # 发送方 + user: example@example.com # 发送邮件的账户 同发送方 + passwd: xxxxxxxxxxxxxx # 发送邮件的密码(非邮件账户密码) + smtp: smtp.163.com # smtp 邮件服务器 + port: 465 # 端口 ssl 为 True 时填 465 + ssl: True # 是否使用 ssl 加密 + # 配置收件人,同发送方 + to: [['收件人1名称', example1@163.com'], ['收件人2名称', example2@163.com']] + +# 需要进行剧集信息搜索的分类名称(qBittorrent的分类) +bangumiCategory: Bangumi + +# 媒体库的地址与图标url +mediaUrl: https://media.example.com +mediaIcon: https://picbed.example.com/media.png diff --git a/generateMailContent.py b/generateMailContent.py new file mode 100644 index 0000000..446d55a --- /dev/null +++ b/generateMailContent.py @@ -0,0 +1,17 @@ +import re +from functools import partial + +def getTemplate(filePath): + file = open(filePath, encoding='utf-8') + fileData = file.read() + file.close() + return fileData + +def replaceStr(data, m): + print(m.group(0)) + param = m.group(0)[2:-2] + return '{}'.format(data.get(param)) + +def generate(templatePath, data): + dataPat = re.compile(r'\{\{(\w*)\}\}') + return dataPat.sub(partial(replaceStr, data), getTemplate(templatePath)) diff --git a/loadYaml.py b/loadYaml.py new file mode 100644 index 0000000..6718a86 --- /dev/null +++ b/loadYaml.py @@ -0,0 +1,9 @@ +import yaml + +def loadYaml(path): + file = open(path, encoding='utf-8') + configData = file.read() + file.close() + + config = yaml.safe_load(configData) + return config \ No newline at end of file diff --git a/mail.py b/mail.py new file mode 100644 index 0000000..d94eefe --- /dev/null +++ b/mail.py @@ -0,0 +1,46 @@ +from loadYaml import loadYaml +import smtplib +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.utils import formataddr + +config = loadYaml('config.yaml') +emailConfig = config.get('email') + +sender = emailConfig.get('from') +user = emailConfig.get('user') +passwd = emailConfig.get('passwd') +receivers = emailConfig.get('to') + +receiversEmailAddress = [] +receiversAddr = [] +for recipient in receivers: + receiversEmailAddress.append(recipient[1]) + receiversAddr.append(formataddr(recipient)) + +def send(subject, content, sendAll): + message = MIMEMultipart('related') + message['From'] = formataddr(sender) + if sendAll: + message['To'] = ", ".join(receiversAddr) + else: + message['To'] = receiversAddr[0] + + + message['Subject'] = subject + + msgAlternative = MIMEMultipart('alternative') + message.attach(msgAlternative) + + msgAlternative.attach(MIMEText(content, 'html', 'utf-8')) + + server = smtplib.SMTP_SSL('smtp.163.com', 465) + server.login(user, passwd) + + if sendAll: + server.sendmail(user, receiversEmailAddress, message.as_string()) + else: + server.sendmail(user, [receiversEmailAddress[0]], message.as_string()) + + server.quit() + print('邮件发送成功') \ No newline at end of file diff --git a/template/email1.html b/template/email1.html new file mode 100644 index 0000000..cb7a6f8 --- /dev/null +++ b/template/email1.html @@ -0,0 +1,40 @@ + + + + + + + + {{title}} + + + +

{{name}}

+ + + + + + + +
+
+ +

{{desc}}

+
+
+
+

{{summary}}

+
+
+
+ + + +
+ + + \ No newline at end of file diff --git a/template/email2.html b/template/email2.html new file mode 100644 index 0000000..726c5c9 --- /dev/null +++ b/template/email2.html @@ -0,0 +1,19 @@ + + + + + + + + {{title}} + + + +

{{name}}

+
+

下载路径:{{path}}

+

请求错误

+

{{errorMsgs}}

+ + + \ No newline at end of file diff --git a/template/email3.html b/template/email3.html new file mode 100644 index 0000000..1776978 --- /dev/null +++ b/template/email3.html @@ -0,0 +1,17 @@ + + + + + + + + {{title}} + + + +

{{name}}

+
+

下载路径:{{path}}

+ + + \ No newline at end of file