前言
freenom域名越来越难申请了,且续期都是未知数,为防止自动续期失败导致域名丢失,特制作一个定期播报脚本,包含续期及到期信息监控,机器人消息支持企业微信和telegram,本文重点强调 telegram 播报的实现。
效果展示
telegram接收消息条件准备
机器人申请
搜索 BotFather 进入聊天页面,输入指令 /newbot
按照提示输入机器人名称及账号ID即可。
获取Token
将上图中马赛克部分保留下来,那就是机器人发送用的 token 。
频道准备
这里采用频道来接收机器人播报消息,专门新建一个家庭基建内部频道。
获取频道 chat id
chat_id 是接收消息的目标对象,必要条件,访问网址 https://api.telegram.org/bot<your_token>/getUpdates ,your_token 替换为上边保留的token即可,找到形如如下截图中类型为 channel 的 id 号码。
脚本实现
封装一个类,接收多个 freenom 账号,遍历登录获取续期信息,同时判断到期时间是否为 2 周内,自动执行续期请求。按需进行机器人播报续期消息,未实现钉钉,仅实现了 telegram 和企业微信。请根据实际情况填充脚本最后那里的配置信息。考虑到 freenom 官方新增了验证码机制,脚本引入了休眠时间,可能执行时间较长,请耐心等待。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Project : tools
# @File : freenom_alert.py
# @Software: PyCharm
# @Author : 易雾君
# @Email : [email protected]
# @公众号 : 易雾山庄
# @Site : https://www.evling.tech
# @Describe : 家庭基建,生活乐享. # @Time : 2023/9/24 18:17
import re
import time
import datetime
import requests
from telebot import TeleBot
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
class FreeNom:
def __init__(self):
self.session = requests.session()
self.session.headers.update({'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/79.0.3945.130 Safari/537.36'})
self.domain_info = dict()
self.timeout = 60
self.sleep = 20
def login(self, username:str, password:str, retry:int=10):
result = False
self.session.headers.update({
'content-type': 'application/x-www-form-urlencoded',
'referer': 'https://my.freenom.com/clientarea.php'
})
print(f'login: {username}')
url = 'https://my.freenom.com/dologin.php'
data = {'username': username, 'password': password}
for i in range(retry):
try:
res = self.session.post(url, data=data, timeout=self.timeout, verify=False)
if res.status_code == 200:
res.content.decode()
result = True
break
print(f'retry {i+1}')
time.sleep(self.sleep)
except:
pass
return result
def run(self, accounts:dict):
for username,password in accounts.items():
self.domain_info[username] = dict()
if not self.login(username, password):
print('login failed')
continue
try:
url = 'https://my.freenom.com/domains.php?a=renewals'
self.session.headers.update({'referer': 'https://my.freenom.com/clientarea.php'})
res = self.session.get(url, timeout=self.timeout, verify=False)
login_status_ptn = re.compile('<a href="logout.php">Logout</a>', re.I)
if not re.search(login_status_ptn, res.text):
print('get login status failed')
continue
token_ptn = re.compile('name="token" value="(.*?)"', re.I)
match = re.search(token_ptn, res.text)
if not match:
print('get page token failed')
return
token = match.group(1)
domain_info_ptn = re.compile(
r'<tr><td>(.*?)</td><td>[^<]+</td><td>[^<]+<span class="[^<]+>(\d+?).Days</span>[^&]+&domain=(\d+?)">.*?</tr>',
re.I)
domains = re.findall(domain_info_ptn, res.text)
for domain, days, renewal_id in domains:
days = int(days)
if days <= 14:
self.session.headers.update({
'referer': f'https://my.freenom.com/domains.php?a=renewdomain&domain={renewal_id}',
'content-type': 'application/x-www-form-urlencoded'
})
url = 'https://my.freenom.com/domains.php?submitrenewals=true'
data = {
'token': token,
'renewalid': renewal_id,
f'renewalperiod[{renewal_id}]': '12M',
'paymentmethod': 'credit'
}
res = self.session.post(url, data=data, timeout=self.timeout, verify=False)
print(domain, '续期成功' if res.text.find('Order Confirmation') != -1 else '续期失败')
print(f'{domain} 还有 {days} 天续期')
self.domain_info[username][domain] = f'还有 {days} 天续期'
except:
pass
time.sleep(self.sleep)
def send_wecom(self, token:str, title:str, msg_info:dict=None, retry:int=3):
if not msg_info:
msg_info = self.domain_info
url = f'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={token}'
self.session.headers.update({'Content-Type': 'application/json'})
t0 = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = ''
for account,item in msg_info.items():
msg += f'**{account}**\n'
for domain,days in item.items():
msg += f'- {domain}: {days}\n'
msg += '\n'
data = {
'msgtype': 'markdown',
'markdown': {
"content": f"#### **{title}**\n"
f"> **检查时间:**\n{t0}\n\n"
f"> **续期信息:**\n\n{msg}"
}
}
for i in range(retry):
try:
self.session.post(url, json=data, timeout=self.timeout, verify=False)
break
except:
pass
def send_telegram(self, token:str, chat_id:str, title:str, msg_info:dict=None, tags:list=None, bot_author='evling', foot='[易雾山庄](https://www.evling.tech)'):
if not msg_info:
msg_info = self.domain_info
bot = TeleBot(token)
msg = f'*【{title}】*\n'
if tags:
msg += ' '.join([f'#{x}' for x in tags]) + '\n\n'
else:
msg += '\n'
msg += f'*检查时间*: {datetime.datetime.now().strftime("%Y-%m-%d %X")}\n*续期详情*: \n\n'
for account, item in msg_info.items():
msg += f'*{account}*\n'
for domain, days in item.items():
msg += f'{domain}: {days}\n'
msg += '\n'
if bot_author:
msg += f'*BotAuthor*: @{bot_author}\n'
if foot:
msg += foot
bot.send_message(chat_id, msg, parse_mode='Markdown')
if __name__ == '__main__':
accounts = {'user1': 'pass1',
'user2': 'pass2',
'user3': 'pass3'}
freenom = FreeNom()
freenom.run(accounts)
# freenom.send_wecom(token='your_wecom_bot_token', title='域名续期播报')
freenom.send_telegram(token='your_telegram_bot_token', chat_id='your_telegram_tunnel_id', title='域名续期播报', tags=['家庭基建', '域名续期', '免费域名', 'freenom'])
配置 crontab 定时任务,每天早上 9 点进行播报。
# freenom auto monitor
59 8 * * * /usr/bin/python3 /data/tools/telegram-bots/freenom_alert.py
总结
telegram 机器人的功能还很丰富,后边有机会继续分享。