ck_hostloc.HOSTLOC.log_my_ip()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 10
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nop 0
dl 0
loc 10
rs 9.9
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2
"""
3
cron: 48 */12 * * *
4
new Env('HOSTLOC');
5
"""
6
7
import random
8
import re
9
import textwrap
10
import time
11
12
import requests
13
from pyaes import AESModeOfOperationCBC
14
from requests import Session as req_Session
15
16
from notify_mtr import send
17
from utils import get_data
18
19
desp = ""  # 空值
20
21
22
def log(info: str):
23
    global desp
24
    desp = desp + info + "\n"
25
26
27
class HOSTLOC:
28
    def __init__(self, check_items):
29
        self.check_items = check_items
30
        self.home_page = "https://hostloc.com/forum.php"
31
32
    # 随机生成用户空间链接
33
    @staticmethod
34
    def randomly_gen_uspace_url() -> list:
35
        url_list = []
36
        # 访问小黑屋用户空间不会获得积分、生成的随机数可能会重复,这里多生成两个链接用作冗余
37
        for _ in range(12):
38
            uid = random.randint(10000, 50000)
39
            url = f"https://hostloc.com/space-uid-{uid}.html"
40
            url_list.append(url)
41
        return url_list
42
43
    # 使用Python实现防CC验证页面中JS写的的toNumbers函数
44
    @staticmethod
45
    def toNumbers(secret: str) -> list:
46
        return [int(value, 16) for value in textwrap.wrap(secret, 2)]
47
48
    # 不带Cookies访问论坛首页,检查是否开启了防CC机制,将开启状态、AES计算所需的参数全部放在一个字典中返回
49
    def check_anti_cc(self) -> dict:
50
        result_dict = {}
51
        headers = {
52
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
53
            "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
54
        }
55
        home_page = self.home_page
56
        r = requests.get(home_page, headers=headers)
57
        aes_keys = re.findall(r'toNumbers\("(.*?)"\)', r.text)
58
        cookie_name = re.findall('cookie="(.*?)="', r.text)
59
60
        if len(aes_keys) != 0:  # 开启了防CC机制
61
            log("检测到防 CC 机制开启!")
62
            if (
63
                len(aes_keys) != 3 or len(cookie_name) != 1
64
            ):  # 正则表达式匹配到了参数,但是参数个数不对(不正常的情况)
65
                result_dict["ok"] = 0
66
            else:  # 匹配正常时将参数存到result_dict中
67
                result_dict["ok"] = 1
68
                result_dict["cookie_name"] = cookie_name[0]
69
                result_dict["a"] = aes_keys[0]
70
                result_dict["b"] = aes_keys[1]
71
                result_dict["c"] = aes_keys[2]
72
73
        return result_dict
74
75
    # 在开启了防CC机制时使用获取到的数据进行AES解密计算生成一条Cookie(未开启防CC机制时返回空Cookies)
76
    def gen_anti_cc_cookies(self) -> dict:
77
        cookies = {}
78
        anti_cc_status = self.check_anti_cc()
79
80
        if anti_cc_status:  # 不为空,代表开启了防CC机制
81
            if anti_cc_status["ok"] == 0:
82
                log("防 CC 验证过程所需参数不符合要求,页面可能存在错误!")
83
            else:  # 使用获取到的三个值进行AES Cipher-Block Chaining解密计算以生成特定的Cookie值用于通过防CC验证
84
                log("自动模拟计尝试通过防 CC 验证")
85
                a = bytes(self.toNumbers(anti_cc_status["a"]))
86
                b = bytes(self.toNumbers(anti_cc_status["b"]))
87
                c = bytes(self.toNumbers(anti_cc_status["c"]))
88
                cbc_mode = AESModeOfOperationCBC(a, b)
89
                res = cbc_mode.decrypt(c)
90
91
                name = anti_cc_status["cookie_name"]
92
                cookies[name] = res.hex()
93
94
        return cookies
95
96
    # 登录账户
97
    def login(self, username: str, password: str) -> req_Session:
98
        headers = {
99
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
100
            "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
101
            "origin": "https://hostloc.com",
102
            "referer": self.home_page,
103
        }
104
        login_url = (
105
            "https://hostloc.com/member.php?mod=logging&action=login&"
106
            "loginsubmit=yes&infloat=yes&lssubmit=yes&inajax=1"
107
        )
108
        login_data = {
109
            "fastloginfield": "username",
110
            "username": username,
111
            "password": password,
112
            "quickforward": "yes",
113
            "handlekey": "ls",
114
        }
115
116
        s = req_Session()
117
        s.headers.update(headers)
118
        s.cookies.update(self.gen_anti_cc_cookies())
119
        r = s.post(url=login_url, data=login_data)
120
        r.raise_for_status()
121
        return s
122
123
    # 通过抓取用户设置页面的标题检查是否登录成功
124
    @staticmethod
125
    def check_login_status(s: req_Session, number_c: int) -> bool:
126
        test_url = "https://hostloc.com/home.php?mod=spacecp"
127
        r = s.get(test_url)
128
        r.raise_for_status()
129
        r.encoding = "utf-8"
130
        test_title = re.findall(r"<title>(.*?)</title>", r.text)
131
132
        if len(test_title) != 0:  # 确保正则匹配到了内容,防止出现数组索引越界的情况
133
            if test_title[0] != "个人资料 -  全球主机交流论坛 -  Powered by Discuz!":
134
                log(f"第 {number_c} 个帐户登录失败!")
135
                return False
136
            log(f"第 {number_c} 个帐户登录成功!")
137
            return True
138
        log("无法在用户设置页面找到标题,该页面存在错误或被防 CC 机制拦截!")
139
        return False
140
141
    # 抓取并打印输出账户当前积分
142
    def log_current_points(self, s: req_Session):
143
        test_url = self.home_page
144
        r = s.get(test_url)
145
        r.raise_for_status()
146
        r.encoding = "utf-8"
147
        points = re.findall(r"积分: (\d+)", r.text)
148
149
        if len(points) != 0:  # 确保正则匹配到了内容,防止出现数组索引越界的情况
150
            log(f"帐户当前积分:{points[0]}")
151
        else:
152
            log("无法获取帐户积分,可能页面存在错误或者未登录!")
153
        time.sleep(5)
154
155
    # 依次访问随机生成的用户空间链接获取积分
156
    def get_points(self, s: req_Session, number_c: int):
157
        if self.check_login_status(s, number_c):
158
            self.log_current_points(s)  # 打印账户当前积分
159
            url_list = self.randomly_gen_uspace_url()
160
            # 依次访问用户空间链接获取积分,出现错误时不中断程序继续尝试访问下一个链接
161
            for i, url in enumerate(url_list):
162
                try:
163
                    r = s.get(url)
164
                    r.raise_for_status()
165
                    log(f"第 {str(i + 1)} 个用户空间链接访问成功")
166
                    time.sleep(5)  # 每访问一个链接后休眠5秒,以避免触发论坛的防CC机制
167
                except Exception as e:
168
                    log(f"链接访问异常:{str(e)}")
169
            self.log_current_points(s)  # 再次打印账户当前积分
170
        else:
171
            log("请检查你的帐户是否正确!")
172
173
    # 打印输出当前ip地址
174
    @staticmethod
175
    def log_my_ip():
176
        api_url = "https://api.ipify.org/"
177
        try:
178
            r = requests.get(url=api_url)
179
            r.raise_for_status()
180
            r.encoding = "utf-8"
181
            log(f"当前使用 ip 地址:{r.text}")
182
        except Exception as e:
183
            log(f"获取当前 ip 地址失败:{str(e)}")
184
185
    def main(self):
186
        for i, check_item in enumerate(self.check_items):
187
            username = check_item.get("username")
188
            password = check_item.get("password")
189
            self.log_my_ip()
190
            log(f"共检测到 {len(self.check_items)} 个帐户,开始获取积分")
191
            log("*" * 12)
192
193
            try:
194
                s = self.login(username, password)
195
                self.get_points(s, i + 1)
196
                log("*" * 12)
197
            except Exception as e:
198
                log(f"程序执行异常:{str(e)}")
199
                log("*" * 12)
200
201
        log("程序执行完毕,获取积分过程结束")
202
        return desp
203
204
205
if __name__ == "__main__":
206
    _data = get_data()
207
    _check_items = _data.get("HOSTLOC", [])
208
    result = HOSTLOC(check_items=_check_items).main()
209
    send("HOSTLOC", result)
210