├── .deepsource.toml ├── .gitchangelog.rc ├── .gitignore ├── .sourcery.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── api_fast_cn.sh ├── api_hax.py ├── api_json52toml.sh ├── api_leetcode.py ├── api_motto.py ├── api_news.py ├── api_ran_time.py ├── api_rssbot.py ├── api_weather.py ├── check.sample.toml ├── checkin.js ├── checkin_ran.js ├── checkinjs.js ├── checkins_pkg.js ├── checkinsh.js ├── city.json ├── ck_acfun.py ├── ck_airport.py ├── ck_aqc.js ├── ck_baidu_url_submit.py ├── ck_bilibili.py ├── ck_bilibili_helper.sh ├── ck_ccava.py ├── ck_cloud189.py ├── ck_csdn.py ├── ck_du163.js ├── ck_duokan.py ├── ck_enshan.py ├── ck_euserv.py ├── ck_everphoto.py ├── ck_fmapp.py ├── ck_freenom.py ├── ck_game163.py ├── ck_glados.py ├── ck_haidilao.py ├── ck_heytap.py ├── ck_hifini.py ├── ck_hlx.py ├── ck_hostloc.py ├── ck_juejin.py ├── ck_kgqq.py ├── ck_lenovo.js ├── ck_meizu.py ├── ck_mimotion.py ├── ck_nga.py ├── ck_oneplusbbs.py ├── ck_picacomic.py ├── ck_pojie.py ├── ck_site.py ├── ck_smzdm.js ├── ck_smzdm.py ├── ck_smzdm_app.py ├── ck_sspanel.sh ├── ck_tieba.py ├── ck_toolu.py ├── ck_v2ex.py ├── ck_weibo.py ├── ck_wps.py ├── ck_www2nzz.py ├── ck_wzyd.py ├── ck_youdao.py ├── ck_zhiyoo.py ├── cpm ├── cpmConfig.pm ├── dailycheckin.json ├── env.sample ├── ins_pkg.sh ├── ins_selenium.sh ├── notify.js ├── notify.sample.toml ├── notify.sh ├── notify_mtr.py ├── notify_mtr_env.py ├── package-lock.json ├── package.json ├── pyproject.toml ├── rss.sample.db ├── rss.sample.sql ├── user-agent.json ├── utils.js ├── utils.py ├── utils_env.py ├── utils_env.sh ├── utils_json52toml.pl ├── utils_models.py ├── utils_task.js ├── utils_tmp.py └── utils_ver.py /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "python" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | runtime_version = "3.x.x" 9 | type_checker = "mypy" 10 | max_line_length = 120 11 | skip_doc_coverage = ["module", "magic", "init", "class", "nonpublic"] 12 | 13 | [[transformers]] 14 | name = "black" 15 | enabled = true 16 | 17 | [[transformers]] 18 | name = "isort" 19 | enabled = true 20 | -------------------------------------------------------------------------------- /.gitchangelog.rc: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8; mode: python -*- 2 | 3 | ignore_regexps = [ 4 | r'@minor', r'!minor', 5 | r'@cosmetic', r'!cosmetic', 6 | r'@refactor', r'!refactor', 7 | r'@wip', r'!wip', 8 | r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:', 9 | r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:', 10 | r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$', 11 | r'^$', ## ignore commits with empty messages 12 | ] 13 | 14 | 15 | ## ``section_regexps`` is a list of 2-tuples associating a string label and a 16 | ## list of regexp 17 | ## 18 | ## Commit messages will be classified in sections thanks to this. Section 19 | ## titles are the label, and a commit is classified under this section if any 20 | ## of the regexps associated is matching. 21 | ## 22 | ## Please note that ``section_regexps`` will only classify commits and won't 23 | ## make any changes to the contents. So you'll probably want to go check 24 | ## ``subject_process`` (or ``body_process``) to do some changes to the subject, 25 | ## whenever you are tweaking this variable. 26 | ## 27 | section_regexps = [ 28 | ('Feature', [ 29 | r'^:(racehorse|rocket|sparkles|tada):\s*([^\n]*)$', 30 | ]), 31 | ('Fix', [ 32 | r'^:(ambulance|bug|fire|hankey|wrench):\s*([^\n]*)$', 33 | ]), 34 | ('Docs', [ 35 | r'^:(memo|mute|speaker|speech_balloon):\s*([^\n]*)$', 36 | ]), 37 | ('Code Style', [ 38 | r'^:(art|building_construction|zap):\s*([^\n]*)$', 39 | ]), 40 | ('Refactor Functions', [ 41 | r'^:(hammer|recycle):\s*([^\n]*)$', 42 | ]), 43 | 44 | ('Other', None ## Match all lines 45 | ), 46 | 47 | ] 48 | 49 | 50 | ## ``body_process`` is a callable 51 | ## 52 | ## This callable will be given the original body and result will 53 | ## be used in the changelog. 54 | ## 55 | ## Available constructs are: 56 | ## 57 | ## - any python callable that take one txt argument and return txt argument. 58 | ## 59 | ## - ReSub(pattern, replacement): will apply regexp substitution. 60 | ## 61 | ## - Indent(chars=" "): will indent the text with the prefix 62 | ## Please remember that template engines gets also to modify the text and 63 | ## will usually indent themselves the text if needed. 64 | ## 65 | ## - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns 66 | ## 67 | ## - noop: do nothing 68 | ## 69 | ## - ucfirst: ensure the first letter is uppercase. 70 | ## (usually used in the ``subject_process`` pipeline) 71 | ## 72 | ## - final_dot: ensure text finishes with a dot 73 | ## (usually used in the ``subject_process`` pipeline) 74 | ## 75 | ## - strip: remove any spaces before or after the content of the string 76 | ## 77 | ## - SetIfEmpty(msg="No commit message."): will set the text to 78 | ## whatever given ``msg`` if the current text is empty. 79 | ## 80 | ## Additionally, you can `pipe` the provided filters, for instance: 81 | #body_process = Wrap(regexp=r'\n(?=\w+\s*:)') | Indent(chars=" ") 82 | #body_process = Wrap(regexp=r'\n(?=\w+\s*:)') 83 | #body_process = noop 84 | body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip 85 | 86 | 87 | ## ``subject_process`` is a callable 88 | ## 89 | ## This callable will be given the original subject and result will 90 | ## be used in the changelog. 91 | ## 92 | ## Available constructs are those listed in ``body_process`` doc. 93 | subject_process = (strip | 94 | ReSub(r'^(:[a-z]*:)\s*([^\n]*)$', r'\1 : \2') | 95 | SetIfEmpty("No commit message.") | ucfirst | final_dot) 96 | 97 | 98 | ## ``tag_filter_regexp`` is a regexp 99 | ## 100 | ## Tags that will be used for the changelog must match this regexp. 101 | ## 102 | tag_filter_regexp = r'^(\d+)-(\d+)-(...)$' 103 | 104 | 105 | ## ``unreleased_version_label`` is a string or a callable that outputs a string 106 | ## 107 | ## This label will be used as the changelog Title of the last set of changes 108 | ## between last valid tag and HEAD if any. 109 | unreleased_version_label = "(unreleased)" 110 | 111 | 112 | ## ``output_engine`` is a callable 113 | ## 114 | ## This will change the output format of the generated changelog file 115 | ## 116 | ## Available choices are: 117 | ## 118 | ## - rest_py 119 | ## 120 | ## Legacy pure python engine, outputs ReSTructured text. 121 | ## This is the default. 122 | ## 123 | ## - mustache() 124 | ## 125 | ## Template name could be any of the available templates in 126 | ## ``templates/mustache/*.tpl``. 127 | ## Requires python package ``pystache``. 128 | ## Examples: 129 | ## - mustache("markdown") 130 | ## - mustache("restructuredtext") 131 | ## 132 | ## - makotemplate() 133 | ## 134 | ## Template name could be any of the available templates in 135 | ## ``templates/mako/*.tpl``. 136 | ## Requires python package ``mako``. 137 | ## Examples: 138 | ## - makotemplate("restructuredtext") 139 | ## 140 | #output_engine = rest_py 141 | #output_engine = mustache("restructuredtext") 142 | output_engine = mustache("markdown") 143 | #output_engine = makotemplate("restructuredtext") 144 | 145 | 146 | ## ``include_merge`` is a boolean 147 | ## 148 | ## This option tells git-log whether to include merge commits in the log. 149 | ## The default is to include them. 150 | include_merge = False 151 | 152 | 153 | ## ``log_encoding`` is a string identifier 154 | ## 155 | ## This option tells gitchangelog what encoding is outputed by ``git log``. 156 | ## The default is to be clever about it: it checks ``git config`` for 157 | ## ``i18n.logOutputEncoding``, and if not found will default to git's own 158 | ## default: ``utf-8``. 159 | #log_encoding = 'utf-8' 160 | 161 | 162 | ## ``publish`` is a callable 163 | ## 164 | ## Sets what ``gitchangelog`` should do with the output generated by 165 | ## the output engine. ``publish`` is a callable taking one argument 166 | ## that is an interator on lines from the output engine. 167 | ## 168 | ## Some helper callable are provided: 169 | ## 170 | ## Available choices are: 171 | ## 172 | ## - stdout 173 | ## 174 | ## Outputs directly to standard output 175 | ## (This is the default) 176 | ## 177 | ## - FileInsertAtFirstRegexMatch(file, pattern, idx=lamda m: m.start()) 178 | ## 179 | ## Creates a callable that will parse given file for the given 180 | ## regex pattern and will insert the output in the file. 181 | ## ``idx`` is a callable that receive the matching object and 182 | ## must return a integer index point where to insert the 183 | ## the output in the file. Default is to return the position of 184 | ## the start of the matched string. 185 | ## 186 | ## - FileRegexSubst(file, pattern, replace, flags) 187 | ## 188 | ## Apply a replace inplace in the given file. Your regex pattern must 189 | ## take care of everything and might be more complex. Check the README 190 | ## for a complete copy-pastable example. 191 | ## 192 | publish = FileInsertAtFirstRegexMatch( 193 | "CHANGELOG.md", 194 | r'##\s+(?P(\d+)-(\d+)-(...))\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n' 195 | ) 196 | #publish = stdout 197 | 198 | 199 | ## ``revs`` is a list of callable or a list of string 200 | ## 201 | ## callable will be called to resolve as strings and allow dynamical 202 | ## computation of these. The result will be used as revisions for 203 | ## gitchangelog (as if directly stated on the command line). This allows 204 | ## to filter exaclty which commits will be read by gitchangelog. 205 | ## 206 | ## To get a full documentation on the format of these strings, please 207 | ## refer to the ``git rev-list`` arguments. There are many examples. 208 | ## 209 | ## Using callables is especially useful, for instance, if you 210 | ## are using gitchangelog to generate incrementally your changelog. 211 | ## 212 | ## Some helpers are provided, you can use them:: 213 | ## 214 | ## - FileFirstRegexMatch(file, pattern): will return a callable that will 215 | ## return the first string match for the given pattern in the given file. 216 | ## If you use named sub-patterns in your regex pattern, it'll output only 217 | ## the string matching the regex pattern named "rev". 218 | ## 219 | ## - Caret(rev): will return the rev prefixed by a "^", which is a 220 | ## way to remove the given revision and all its ancestor. 221 | ## 222 | ## Please note that if you provide a rev-list on the command line, it'll 223 | ## replace this value (which will then be ignored). 224 | ## 225 | ## If empty, then ``gitchangelog`` will act as it had to generate a full 226 | ## changelog. 227 | ## 228 | ## The default is to use all commits to make the changelog. 229 | #revs = ["^1.0.3", ] 230 | #revs = [ 231 | # Caret( 232 | # FileFirstRegexMatch( 233 | # "CHANGELOG.rst", 234 | # r"(?P[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n")), 235 | # "HEAD" 236 | #] 237 | revs = [] -------------------------------------------------------------------------------- /.sourcery.yaml: -------------------------------------------------------------------------------- 1 | ignore: [] 2 | 3 | refactor: 4 | include: [] 5 | skip: [] 6 | rule_types: 7 | - refactoring 8 | - suggestion 9 | - comment 10 | python_version: '3.6' 11 | 12 | rules: [] 13 | 14 | metrics: 15 | quality_threshold: 25.0 16 | 17 | github: 18 | labels: [] 19 | ignore_labels: 20 | - sourcery-ignore 21 | request_review: author 22 | sourcery_branch: sourcery/{base_branch} 23 | 24 | clone_detection: 25 | min_lines: 3 26 | min_duplicates: 2 27 | identical_clones_only: false 28 | 29 | proxy: 30 | no_ssl_verify: false 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Oreomeow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /api_fast_cn.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # shellcheck disable=SC2188 4 | <<'COMMENT' 5 | cron: 45 12 */7 * * 6 | new Env('国内加速'); 7 | COMMENT 8 | 9 | # alpine 换源 10 | sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories 11 | 12 | # Python 换源 13 | mkdir -p /root/.config/pip 14 | cat >/root/.config/pip/pip.conf < 1 38 | else v 39 | ) 40 | vps_list.append((zone, sum_)) 41 | for k_v in vps_list: 42 | k, v = k_v 43 | vps_dict.setdefault(k, []).append(v) 44 | return "".join(f">>{k}-" + ", ".join(v) + "\n" for k, v in vps_dict.items()) 45 | 46 | def get_data_center(self, url, vir=False): 47 | html_text = self.check(url) 48 | soup = BeautifulSoup(html_text, "html.parser") 49 | ctr_list = [x.text for x in soup("option", value=re.compile(r"^[A-Z]{2,}-"))] 50 | ctr_str = "\n".join(ctr_list) 51 | if vir: 52 | ctr_list = [ 53 | (c.split(" (")[1].rstrip(")"), c.split(" (")[0]) for c in ctr_list 54 | ] 55 | ctr_dict = {} 56 | for k_v in ctr_list: 57 | k, v = k_v 58 | ctr_dict.setdefault(k, []).append(v) 59 | ctr_str = "".join( 60 | f"★{k}★ " + ", ".join(v) + "\n" for k, v in ctr_dict.items() 61 | ) 62 | return ctr_str 63 | 64 | def main(self): 65 | hax_str = self.get_server_info("https://hax.co.id/data-center") 66 | hax_stat = f"[🛰Hax Stats / Hax 开通数据]\n{hax_str}\n" 67 | vir_str = self.get_data_center("https://hax.co.id/create-vps", True) 68 | woiden_str = self.get_data_center("https://woiden.id/create-vps") 69 | data_center = ( 70 | f"[🚩Available Centers / 可开通区域]\n" 71 | f'---------- Hax ----------\n' 72 | f"{vir_str}" 73 | f'---------- Woiden ----------\n' 74 | f"{woiden_str}\n" 75 | ) 76 | return hax_stat + data_center 77 | 78 | 79 | if __name__ == "__main__": 80 | _data = get_data() 81 | hax = _data.get("HAX") 82 | if hax: 83 | result = Hax().main() 84 | send("Hax", result) 85 | -------------------------------------------------------------------------------- /api_json52toml.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # shellcheck disable=SC2188 4 | <<'COMMENT' 5 | cron: 45 */1 * * * 6 | new Env('JSON5toTOML 工具'); 7 | COMMENT 8 | 9 | . utils_env.sh 10 | get_some_path 11 | 12 | json52toml() { 13 | if [ -f "${CONF_PATH}/config.sh" ]; then 14 | sed -i '/^RepoFileExtensions/c RepoFileExtensions="js pl py sh ts"' "${CONF_PATH}/config.sh" 15 | cp -f "${REPO_PATH}/OreosLab_checkinpanel_master/check.sample.toml" "${CONF_PATH}/check.template.toml" 16 | fi 17 | find . -type f -name '*utils_json52toml.pl' -exec perl {} \; 18 | } 19 | json52toml 20 | -------------------------------------------------------------------------------- /api_leetcode.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :author @wangwangit 4 | cron: 10 10 * * * 5 | new Env('LeetCode 每日一题'); 6 | """ 7 | 8 | import requests 9 | 10 | from notify_mtr import send 11 | from utils import get_data 12 | 13 | 14 | class LeetCode: 15 | @staticmethod 16 | def main(): 17 | base_url = "https://leetcode-cn.com" 18 | 19 | # 获取今日每日一题的题名(英文) 20 | res = requests.post( 21 | f"{base_url}/graphql", 22 | json={ 23 | "operationName": "questionOfToday", 24 | "variables": {}, 25 | "query": "query questionOfToday { todayRecord { " 26 | "question { questionFrontendId questionTitleSlug __typename } " 27 | "lastSubmission { id __typename } " 28 | "date userStatus __typename } }", 29 | }, 30 | ).json() 31 | title_slug = ( 32 | res.get("data") 33 | .get("todayRecord")[0] 34 | .get("question") 35 | .get("questionTitleSlug") 36 | ) 37 | 38 | # 获取今日每日一题的所有信息 39 | url = f"{base_url}/problems/{title_slug}" 40 | res = requests.post( 41 | f"{base_url}/graphql", 42 | json={ 43 | "operationName": "questionData", 44 | "variables": {"titleSlug": title_slug}, 45 | "query": "query questionData($titleSlug: String!) " 46 | "{ question(titleSlug: $titleSlug) { questionId questionFrontendId " 47 | "boundTopicId title titleSlug content translatedTitle translatedContent" 48 | " isPaidOnly difficulty likes dislikes isLiked similarQuestions " 49 | "contributors { username profileUrl avatarUrl __typename } " 50 | "langToValidPlayground topicTags " 51 | "{ name slug translatedName __typename } " 52 | "companyTagStats codeSnippets { lang langSlug code __typename } " 53 | "stats hints solution { id canSeeDetail __typename } " 54 | "status sampleTestCase metaData judgerAvailable judgeType " 55 | "mysqlSchemas enableRunCode envInfo book " 56 | "{ id bookName pressName source shortDescription fullDescription" 57 | " bookImgUrl pressImgUrl productUrl __typename } " 58 | "isSubscribed isDailyQuestion dailyRecordStatus " 59 | "editorType ugcQuestionId style __typename } }", 60 | }, 61 | ).json() 62 | 63 | # 题目 64 | question = res.get("data").get("question") 65 | # 题号 66 | num = question.get("questionFrontendId") 67 | # 题名(中文) 68 | zh_title = question.get("translatedTitle") 69 | return f'{num}. {zh_title}' 70 | 71 | 72 | if __name__ == "__main__": 73 | _data = get_data() 74 | leetcode = _data.get("LEETCODE") 75 | if leetcode: 76 | result = LeetCode().main() 77 | send("LeetCode 每日一题", result) 78 | -------------------------------------------------------------------------------- /api_motto.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 30 7 * * * 4 | new Env('每日一句'); 5 | """ 6 | 7 | import requests 8 | 9 | from notify_mtr import send 10 | from utils import get_data 11 | 12 | 13 | class Motto: 14 | @staticmethod 15 | def main(): 16 | """从词霸中获取每日一句,带英文 17 | 18 | :return: str 19 | """ 20 | response = requests.get("http://open.iciba.com/dsapi") 21 | if response.status_code != 200: 22 | return "" 23 | res = response.json() 24 | return f'{res["content"]}\n{res["note"]}\n' 25 | 26 | 27 | if __name__ == "__main__": 28 | _data = get_data() 29 | motto = _data.get("MOTTO") 30 | if motto: 31 | result = Motto().main() 32 | send("每日一句", result) 33 | -------------------------------------------------------------------------------- /api_news.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 40 7 * * * 4 | new Env('每日新闻'); 5 | """ 6 | 7 | import re 8 | import traceback 9 | 10 | import requests 11 | 12 | from notify_mtr import send 13 | from utils import get_data 14 | 15 | 16 | class News: 17 | @staticmethod 18 | def parse_data(data, topic): 19 | if not data.get(topic): 20 | return "" 21 | msg = "" 22 | for key, value in data.get(topic).items(): 23 | if key == "content": 24 | for i in value: 25 | msg += str(i) 26 | msg += "\n" 27 | elif ( 28 | value 29 | and type(value) is not bool 30 | and not bool(re.search("[a-z]", str(value))) 31 | ): 32 | msg += str(value) + "\n" 33 | return msg 34 | 35 | def main(self): 36 | msg = "" 37 | try: 38 | res = requests.get("https://news.topurl.cn/api").json() 39 | if res.get("code") == 200: 40 | data = res.get("data") 41 | if data.get("newsList"): 42 | msg += "📮 每日新闻 📮\n" 43 | for no, news_ in enumerate(data.get("newsList"), start=1): 44 | msg += f'{str(no).zfill(2)}. {news_.get("title")}\n' 45 | if data.get("historyList"): 46 | msg += "\n🎬 历史上的今天 🎬\n" 47 | for history in data.get("historyList"): 48 | msg += f'{history.get("event", "")}\n' 49 | msg += "\n🧩 天天成语 🧩\n" + self.parse_data(data, "phrase") 50 | msg += "\n🎻 慧语香风 🎻\n" + self.parse_data(data, "sentence") 51 | msg += "\n🎑 诗歌天地 🎑\n" + self.parse_data(data, "poem") 52 | except Exception: 53 | msg += f"每日新闻: 异常 {traceback.format_exc()}" 54 | return msg 55 | 56 | 57 | if __name__ == "__main__": 58 | _data = get_data() 59 | news = _data.get("NEWS") 60 | if news: 61 | result = News().main() 62 | send("每日新闻", result) 63 | -------------------------------------------------------------------------------- /api_ran_time.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | :author @night-raise from github 4 | cron: 0 0 */7 * * 5 | new Env('随机定时'); 6 | """ 7 | 8 | from abc import ABC 9 | from random import randrange 10 | from typing import Dict, List, Optional 11 | 12 | import requests 13 | 14 | from notify_mtr import send 15 | from utils import get_data 16 | from utils_env import get_env_int 17 | 18 | 19 | class ClientApi(ABC): 20 | def __init__(self): 21 | self.cid = "" 22 | self.sct = "" 23 | self.url = "http://localhost:5700/" 24 | self.twice = False 25 | self.token = "" 26 | self.cron: List[Dict] = [] 27 | 28 | def init_cron(self): 29 | raise NotImplementedError 30 | 31 | def shuffle_cron(self): 32 | raise NotImplementedError 33 | 34 | def run(self): 35 | self.init_cron() 36 | self.shuffle_cron() 37 | 38 | @staticmethod 39 | def get_ran_min() -> str: 40 | return str(randrange(0, 60)) 41 | 42 | def get_ran_hour(self, is_api: bool = False) -> str: 43 | if is_api: 44 | return str(randrange(7, 9)) 45 | if self.twice: 46 | start = randrange(0, 12) 47 | return f"{start},{start + randrange(6, 12)}" 48 | return str(randrange(0, 24)) 49 | 50 | def random_time(self, origin_time: str, command: str): 51 | if "ran_time" in command: 52 | return origin_time 53 | if "rssbot" in command or "hax" in command: 54 | return f"{ClientApi.get_ran_min()} " + " ".join(origin_time.split(" ")[1:]) 55 | is_api = "api" in command 56 | return f"{ClientApi.get_ran_min()} {self.get_ran_hour(is_api)} " + " ".join( 57 | origin_time.split(" ")[2:] 58 | ) 59 | 60 | 61 | class QLClient(ClientApi): 62 | def __init__(self, client_info: Dict): 63 | super().__init__() 64 | if ( 65 | not client_info 66 | or not (cid := client_info.get("client_id")) 67 | or not (sct := client_info.get("client_secret")) 68 | ): 69 | raise ValueError("无法获取 client 相关参数!") 70 | self.cid = cid 71 | self.sct = sct 72 | self.url = client_info.get("url", "http://localhost:5700").rstrip("/") + "/" 73 | self.twice = client_info.get("twice", False) 74 | self.token = requests.get( 75 | url=f"{self.url}open/auth/token", 76 | params={"client_id": self.cid, "client_secret": self.sct}, 77 | ).json()["data"]["token"] 78 | 79 | if not self.token: 80 | raise ValueError("无法获取 token!") 81 | 82 | def init_cron(self): 83 | cron_data = requests.get( 84 | url=f"{self.url}open/crons", 85 | headers={"Authorization": f"Bearer {self.token}"}, 86 | ).json()["data"] 87 | if type(cron_data) == dict and "data" in cron_data.keys(): 88 | cron_data = cron_data["data"] 89 | self.cron = list( 90 | filter( 91 | lambda x: not x.get("isDisabled", 1) 92 | and x.get("command", "").find("OreosLab_checkinpanel_master") != -1, 93 | cron_data, 94 | ) 95 | ) 96 | 97 | def shuffle_cron(self): 98 | for c in self.cron: 99 | json = { 100 | "labels": c.get("labels", None), 101 | "command": c["command"], 102 | "schedule": self.random_time(c["schedule"], c["command"]), 103 | "name": c["name"], 104 | "id": c["id"], 105 | } 106 | requests.put( 107 | url=f"{self.url}open/crons", 108 | json=json, 109 | headers={"Authorization": f"Bearer {self.token}"}, 110 | ) 111 | 112 | 113 | def get_client() -> Optional[QLClient]: 114 | env_type = get_env_int() 115 | if env_type in [5, 6]: 116 | check_data = get_data() 117 | return QLClient(check_data.get("RANDOM", [{}])[0]) 118 | return None 119 | 120 | 121 | def main(): 122 | try: 123 | if client := get_client(): 124 | client.run() 125 | send("随机定时", "处于启动状态的任务定时修改成功!") 126 | else: 127 | send("随机定时", "你的系统不支持运行随机定时!") 128 | except ValueError as e: 129 | send("随机定时", f"配置错误,{e},请检查你的配置文件!") 130 | 131 | 132 | if __name__ == "__main__": 133 | main() 134 | -------------------------------------------------------------------------------- /api_rssbot.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | cron: 22 6-22/2 * * * 4 | new Env('RSS 订阅'); 5 | """ 6 | 7 | from datetime import datetime, timedelta, timezone 8 | from time import mktime 9 | 10 | import feedparser 11 | 12 | from notify_mtr import send 13 | from utils_models import History, Rss, db 14 | 15 | 16 | class RssRobot: 17 | def main(self): 18 | self.remove_old_history() 19 | 20 | rss_list = Rss.select() 21 | post_url_list = [ 22 | rss_history.url 23 | for rss_history in History.select().where( 24 | History.publish_at == datetime.now().strftime("%Y-%m-%d") 25 | ) 26 | ] 27 | 28 | no = 0 29 | msg = "" 30 | for rss in rss_list: 31 | rss_history_list = [] 32 | feed = feedparser.parse(rss.feed) 33 | title = True 34 | c_no = 1 35 | for entry in feed.entries: 36 | pub_t = datetime.fromtimestamp(mktime(entry["published_parsed"])) 37 | 38 | # 此网站单独处理 39 | if rss.url == "https://www.foreverblog.cn": 40 | pub_t = pub_t.replace( 41 | year=datetime.now(timezone.utc).year 42 | ) + timedelta(hours=8) 43 | 44 | elif rss.url == "https://www.zhihu.com": 45 | entry.link = entry.link.split("/answer")[0] 46 | 47 | if ( 48 | entry.link not in post_url_list 49 | and ( 50 | datetime.timestamp(datetime.now(timezone.utc)) 51 | - datetime.timestamp(pub_t) 52 | ) 53 | < rss.before * 86400 54 | ): 55 | if title: 56 | msg += f"{rss.title.strip()}\n" 57 | title = False 58 | msg = f'{msg}{c_no}. {entry.title}\n' 59 | no += 1 60 | c_no += 1 61 | if no % 20 == 0: 62 | send("RSS 订阅", msg) 63 | msg = "" 64 | title = False 65 | rss_history_list.append(History(url=entry.link)) 66 | with db.atomic(): 67 | History.bulk_create(rss_history_list, batch_size=10) 68 | 69 | if no % 20 != 0 and msg: 70 | send("RSS 订阅", msg) 71 | 72 | @staticmethod 73 | def remove_old_history(): 74 | # 只保留最近一周的记录 75 | week_date_range = datetime.now() + timedelta(days=-7) 76 | History.delete().where( 77 | History.publish_at < week_date_range.strftime("%Y-%m-%d") 78 | ).execute() 79 | 80 | 81 | if __name__ == "__main__": 82 | RssRobot().main() 83 | -------------------------------------------------------------------------------- /api_weather.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 30 7 * * * 4 | new Env('天气预报'); 5 | """ 6 | 7 | import json 8 | from os.path import dirname, join 9 | 10 | import requests 11 | 12 | from notify_mtr import send 13 | from utils import get_data 14 | 15 | 16 | class Weather: 17 | def __init__(self, check_items): 18 | self.check_items = check_items 19 | 20 | @staticmethod 21 | def city_map(): 22 | cur_dir = dirname(__file__) 23 | try: 24 | with open(join(cur_dir, "city.json"), "r", encoding="utf-8") as city_file: 25 | city_map = json.load(city_file) 26 | if not city_map: 27 | raise FileNotFoundError 28 | except FileNotFoundError as e: 29 | r = requests.get( 30 | "https://fastly.jsdelivr.net/gh/Oreomeow/checkinpanel@master/city.json" 31 | ) 32 | if r.status_code != 200: 33 | raise ConnectionError("下载 city.json 失败!") from e 34 | city_map = r.json() 35 | with open(join(cur_dir, "city.json"), "w", encoding="utf-8") as city_file: 36 | json.dump(city_map, city_file, ensure_ascii=False) 37 | return city_map 38 | 39 | def main(self): 40 | msg_all = "" 41 | for city_name in self.check_items: 42 | city_code = self.city_map().get(city_name, "101020100") 43 | weather_url = f"http://t.weather.itboy.net/api/weather/city/{city_code}" 44 | r = requests.get(weather_url) 45 | if r.status_code == 200 and r.json().get("status") == 200: 46 | d = r.json() 47 | msg = ( 48 | f'城市:{d["cityInfo"]["parent"]} {d["cityInfo"]["city"]}\n' 49 | f'日期:{d["data"]["forecast"][0]["ymd"]} {d["data"]["forecast"][0]["week"]}\n' 50 | f'天气:{d["data"]["forecast"][0]["type"]}\n' 51 | f'温度:{d["data"]["forecast"][0]["high"]} {d["data"]["forecast"][0]["low"]}\n' 52 | f'湿度:{d["data"]["shidu"]}\n' 53 | f'空气质量:{d["data"]["quality"]}\n' 54 | f'PM2.5:{d["data"]["pm25"]}\n' 55 | f'PM10:{d["data"]["pm10"]}\n' 56 | f'风力风向 {d["data"]["forecast"][0]["fx"]} {d["data"]["forecast"][0]["fl"]}\n' 57 | f'感冒指数:{d["data"]["ganmao"]}\n' 58 | f'温馨提示:{d["data"]["forecast"][0]["notice"]}\n' 59 | f'更新时间:{d["time"]}' 60 | ) 61 | else: 62 | msg = f"{city_name} 天气查询失败!" 63 | msg_all += msg + "\n\n" 64 | return msg_all 65 | 66 | 67 | if __name__ == "__main__": 68 | _data = get_data() 69 | _check_items = _data.get("CITY", []) 70 | result = Weather(check_items=_check_items).main() 71 | send("天气预报", result) 72 | -------------------------------------------------------------------------------- /checkin.js: -------------------------------------------------------------------------------- 1 | // @grant nodejs 2 | let pyname = $env.PYNAME; 3 | console.log(`⏳ 开始执行 ${pyname}`); 4 | $exec(`python3 ${pyname}`, { 5 | cwd: 'script/Shell/checkinpanel', 6 | timeout: 0, 7 | // prettier-ignore 8 | env: { 9 | CHECK_CONFIG: $store.get('CHECK_CONFIG', 'string'), // 自定义 toml 配置文件路径,如 /usr/local/app/script/Lists/config.toml 10 | NOTIFY_CONFIG_PATH: $store.get('NOTIFY_CONFIG_PATH', 'string'), // 自定义通知配置文件路径,如 /usr/locallocal/app/script/Lists/notify.json5 11 | HITOKOTO: $store.get('HITOKOTO', 'boolean'), // 一言,true 为开启,false 为关闭,默认关闭 12 | BARK_PUSH: $store.get('BARK_PUSH', 'string'), // bark IP 或设备码,例:https://api.day.app/xxxxxx 13 | BARK_ARCHIVE: $store.get('BARK_ARCHIVE', 'string'), // bark 推送是否存档,可不填 14 | BARK_GROUP: $store.get('BARK_GROUP', 'string'), // bark 推送分组,可不填 15 | BARK_SOUND: $store.get('BARK_SOUND', 'string'), // bark 推送声音,可不填 16 | CONSOLE: $store.get('CONSOLE', 'string'), // 控制台输出,true 为开启,false 为关闭,默认关闭 17 | DD_BOT_SECRET: $store.get('DD_BOT_SECRET', 'string'), // 钉钉机器人的 DD_BOT_SECRET 18 | DD_BOT_TOKEN: $store.get('DD_BOT_TOKEN', 'string'), // 钉钉机器人的 DD_BOT_TOKEN 19 | FSKEY: $store.get('FSKEY', 'string'), // 飞书机器人的 FSKEY,https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxx 的 xxxxxx 部分 20 | GOBOT_URL: $store.get('GOBOT_URL', 'string'), // go-cqhttp e.g.推送到个人QQ: http://127.0.0.1/send_private_msg 群: http://127.0.0.1/send_group_msg 21 | GOBOT_QQ: $store.get('GOBOT_QQ', 'string'), // go-cqhttp 推送群或者用户。GOBOT_URL 设置 /send_private_msg 时填入 user_id=个人QQ;/send_group_msg 时填入 group_id=QQ群 22 | GOBOT_TOKEN: $store.get('GOBOT_TOKEN', 'string'), // go-cqhttp 的 access_token,可不填 23 | IGOT_PUSH_KEY: $store.get('IGOT_PUSH_KEY', 'string'), // iGot 聚合推送的 IGOT_PUSH_KEY 24 | PUSH_KEY: $store.get('PUSH_KEY', 'string'), // server 酱的 PUSH_KEY,兼容旧版与 Turbo 版 25 | PUSH_PLUS_TOKEN: $store.get('PUSH_PLUS_TOKEN', 'string'), // push+ 微信推送的用户令牌 26 | PUSH_PLUS_USER: $store.get('PUSH_PLUS_USER', 'string'), // push+ 微信推送的群组编码,不填仅推送自己 27 | QMSG_KEY: $store.get('QMSG_KEY', 'string'), // qmsg 酱的 QMSG_KEY 28 | QMSG_TYPE: $store.get('QMSG_TYPE', 'string'), // qmsg 酱的 QMSG_TYPE,推送到群填写 group,默认推送到 QQ 29 | QYWX_AM: $store.get('QYWX_AM', 'string'), // 企业微信应用的 QYWX_AM,参考 http://note.youdao.com/s/HMiudGkb,依次填入 corpid, corpsecret, touser(注:多个成员ID使用 | 隔开), agentid, media_id(选填,不填默认文本消息类型) 30 | QYWX_KEY: $store.get('QYWX_KEY', 'string'), // 企业微信机器人的 QYWX_KEY 31 | TG_BOT_TOKEN: $store.get('TG_BOT_TOKEN', 'string'), // tg 机器人的 TG_BOT_TOKEN 32 | TG_USER_ID: $store.get('TG_USER_ID', 'string'), // tg 机器人的 TG_USER_ID 33 | TG_API_HOST: $store.get('TG_API_HOST', 'string'), // tg api 自建反向代理地址,默认 api.telegram.org,可不填 34 | TG_PROXY_AUTH: $store.get('TG_PROXY_AUTH', 'string'), // tg 代理配置认证参数,可不填 35 | TG_PROXY_HOST: $store.get('TG_PROXY_HOST', 'string'), // tg 机器人的 TG_PROXY_HOST,例:127.0.0.1,可不填 36 | TG_PROXY_PORT: $store.get('TG_PROXY_PORT', 'string') // tg 机器人的 TG_PROXY_PORT,例:1080,可不填 37 | }, 38 | logname: pyname.substring(pyname.indexOf('_') + 1, pyname.lastIndexOf('.')), 39 | from: 'task', 40 | }); 41 | -------------------------------------------------------------------------------- /checkin_ran.js: -------------------------------------------------------------------------------- 1 | // @grant require 2 | // @grant sudo 3 | const parser = require('/usr/local/app/script/Shell/checkinpanel/node_modules/cron-parser'); 4 | 5 | let taskinfo = $task.info(); 6 | let tasklist = []; 7 | let namelist = []; 8 | let idlist = []; 9 | let excluding = $store.get('EXRAND', 'array'); 10 | 11 | main(); 12 | 13 | async function main() { 14 | await getRepList(); 15 | await filterList(); 16 | await getNameList(); 17 | for (let i in tasklist) { 18 | tasklist[i]['time'] = await modifyTime(tasklist[i]['time']); 19 | } 20 | await replaceTask(); 21 | startTask(); 22 | } 23 | 24 | async function getRepList() { 25 | for (let i in taskinfo) { 26 | if (taskinfo[i]['type'] == 'cron' && taskinfo[i]['running'] == true && taskinfo[i]['name'] != '随机定时') { 27 | tasklist.push(taskinfo[i]); 28 | } 29 | } 30 | } 31 | 32 | async function filterList() { 33 | if (excluding != undefined) { 34 | console.log('🗑 排除 EXRAND 变量列表中的任务名'); 35 | tasklist = tasklist.filter(function (i) { 36 | return excluding.indexOf(i['name']) == -1; 37 | }); 38 | } 39 | } 40 | 41 | async function getNameList() { 42 | for (let i in tasklist) { 43 | namelist.push(tasklist[i]['name']); 44 | idlist.push(tasklist[i]['id']); 45 | } 46 | } 47 | 48 | async function modifyTime(cron) { 49 | let interval = parser.parseExpression(cron); 50 | let fields = JSON.parse(JSON.stringify(interval.fields)); 51 | fields.minute = [Math.floor(Math.random() * 60)]; 52 | if (fields.hour.length == 1) { 53 | fields.hour = [Math.floor(Math.random() * 17 + 6)]; 54 | } else { 55 | let add = Math.floor(Math.random() * (fields.hour[1] - fields.hour[0])); 56 | for (let i in fields.hour) { 57 | fields.hour[i] = fields.hour[i] + add; 58 | if (fields.hour[i] >= 24) { 59 | fields.hour[i] = fields.hour[i] - 24; 60 | } 61 | } 62 | } 63 | let modifiedInterval = parser.fieldsToExpression(fields); 64 | let cronString = modifiedInterval.stringify(); 65 | return cronString; 66 | } 67 | 68 | async function replaceTask() { 69 | console.log('⏱ 开始修改运行中的 cron 任务...'); 70 | let res = $task.add(tasklist); 71 | console.log(res); 72 | } 73 | 74 | async function startTask() { 75 | console.log('🎈 启动之前处于运行中的 cron 任务...'); 76 | $task.start(idlist); 77 | console.log('✅ 开始保存修改后的任务列表...'); 78 | let saveres = $task.save(); 79 | console.log(saveres); 80 | } 81 | -------------------------------------------------------------------------------- /checkinjs.js: -------------------------------------------------------------------------------- 1 | // @grant nodejs 2 | let jsname = $env.JSNAME; 3 | console.log(`⏳ 开始执行 ${jsname}`); 4 | $exec(`node ${jsname}`, { 5 | cwd: 'script/Shell/checkinpanel', 6 | timeout: 0, 7 | // prettier-ignore 8 | env: { 9 | NODE_PATH: "/usr/local/lib/node_modules", // Node.js 全局依赖路径 10 | CHECK_CONFIG: $store.get('CHECK_CONFIG', 'string'), // 自定义 toml 配置文件路径,如 /usr/local/app/script/Lists/config.toml 11 | NOTIFY_CONFIG_PATH: $store.get('NOTIFY_CONFIG_PATH', 'string'), // 自定义通知配置文件路径,如 /usr/locallocal/app/script/Lists/notify.json5 12 | BARK_PUSH: $store.get('BARK_PUSH', 'string'), // bark IP 或设备码,例:https://api.day.app/xxxxxx 13 | BARK_GROUP: $store.get('BARK_GROUP', 'string'), // bark 推送分组,可不填 14 | BARK_SOUND: $store.get('BARK_SOUND', 'string'), // bark 推送声音,可不填 15 | DD_BOT_SECRET: $store.get('DD_BOT_SECRET', 'string'), // 钉钉机器人的 DD_BOT_SECRET 16 | DD_BOT_TOKEN: $store.get('DD_BOT_TOKEN', 'string'), // 钉钉机器人的 DD_BOT_TOKEN 17 | GOBOT_URL: $store.get('GOBOT_URL', 'string'), // go-cqhttp e.g.推送到个人QQ: http://127.0.0.1/send_private_msg 群: http://127.0.0.1/send_group_msg 18 | GOBOT_QQ: $store.get('GOBOT_QQ', 'string'), // go-cqhttp 推送群或者用户。GOBOT_URL 设置 /send_private_msg 时填入 user_id=个人QQ;/send_group_msg 时填入 group_id=QQ群 19 | GOBOT_TOKEN: $store.get('GOBOT_TOKEN', 'string'), // go-cqhttp 的 access_token,可不填 20 | IGOT_PUSH_KEY: $store.get('IGOT_PUSH_KEY', 'string'), // iGot 聚合推送的 IGOT_PUSH_KEY 21 | PUSH_KEY: $store.get('PUSH_KEY', 'string'), // server 酱的 PUSH_KEY,兼容旧版与 Turbo 版 22 | PUSH_PLUS_TOKEN: $store.get('PUSH_PLUS_TOKEN', 'string'), // push+ 微信推送的用户令牌 23 | PUSH_PLUS_USER: $store.get('PUSH_PLUS_USER', 'string'), // push+ 微信推送的群组编码,不填仅推送自己 24 | QYWX_AM: $store.get('QYWX_AM', 'string'), // 企业微信应用的 QYWX_AM,参考 http://note.youdao.com/s/HMiudGkb,依次填入 corpid, corpsecret, touser(注:多个成员ID使用 | 隔开), agentid, media_id(选填,不填默认文本消息类型) 25 | QYWX_KEY: $store.get('QYWX_KEY', 'string'), // 企业微信机器人的 QYWX_KEY 26 | TG_BOT_TOKEN: $store.get('TG_BOT_TOKEN', 'string'), // tg 机器人的 TG_BOT_TOKEN 27 | TG_USER_ID: $store.get('TG_USER_ID', 'string'), // tg 机器人的 TG_USER_ID 28 | TG_API_HOST: $store.get('TG_API_HOST', 'string'), // tg api 自建反向代理地址,默认 api.telegram.org,可不填 29 | TG_PROXY_AUTH: $store.get('TG_PROXY_AUTH', 'string'), // tg 代理配置认证参数,可不填 30 | TG_PROXY_HOST: $store.get('TG_PROXY_HOST', 'string'), // tg 机器人的 TG_PROXY_HOST,例:127.0.0.1,可不填 31 | TG_PROXY_PORT: $store.get('TG_PROXY_PORT', 'string') // tg 机器人的 TG_PROXY_PORT,例:1080,可不填 32 | }, 33 | logname: jsname.substring(jsname.indexOf('_') + 1, jsname.lastIndexOf('.')), 34 | from: 'task', 35 | }); 36 | -------------------------------------------------------------------------------- /checkins_pkg.js: -------------------------------------------------------------------------------- 1 | // @grant nodejs 2 | console.log(`⏳ 初始化安装环境依赖开始...`); 3 | $exec(`chmod +x ins_pkg.sh && sh ins_pkg.sh && bash ins_selenium.sh`, { 4 | cwd: 'script/Shell/checkinpanel', 5 | timeout: 0, 6 | cb(data, error) { 7 | error ? console.error(error) : console.log(data); 8 | }, 9 | }); 10 | -------------------------------------------------------------------------------- /checkinsh.js: -------------------------------------------------------------------------------- 1 | // @grant nodejs 2 | let shname = $env.SHNAME; 3 | console.log(`⏳ 开始执行 ${shname}`); 4 | $exec(`bash ${shname}`, { 5 | cwd: 'script/Shell/checkinpanel', 6 | timeout: 0, 7 | // prettier-ignore 8 | env: { 9 | ENV_PATH: $store.get('ENV_PATH', 'string'), // 自定义 .env 配置文件路径,如 /usr/local/app/script/Lists/.env 10 | DD_BOT_SECRET: $store.get('DD_BOT_SECRET', 'string'), // 钉钉机器人的 DD_BOT_SECRET 11 | DD_BOT_TOKEN: $store.get('DD_BOT_TOKEN', 'string'), // 钉钉机器人的 DD_BOT_TOKEN 12 | PUSH_KEY: $store.get('PUSH_KEY', 'string'), // server 酱旧版 PUSH_KEY 13 | PUSH_TURBO_KEY: $store.get('PUSH_KEY', 'string'), // server 酱 Turbo 版 PUSH_KEY 14 | PUSH_PLUS_TOKEN: $store.get('PUSH_PLUS_TOKEN', 'string'), // push+ 微信推送的用户令牌 15 | QMSG_KEY: $store.get('QMSG_KEY', 'string'), // qmsg 酱的 QMSG_KEY 16 | CORPID: $store.get('CORPID', 'string'), // 企业微信 ID 17 | AGENTID: $store.get('AGENTID', 'string'), // 企业微信应用 ID 18 | CORPSECRET: $store.get('CORPSECRET', 'string'), // 企业微信密钥 19 | SRE_TOKEN: $store.get('SRE_TOKEN', 'string'), // SRE24.com 的 SRE_TOKEN,https://push.jwks123.cn 关注公众号后再次点击获取令牌 20 | TG_BOT_TOKEN: $store.get('TG_BOT_TOKEN', 'string'), // tg 机器人的 TG_BOT_TOKEN 21 | TG_USER_ID: $store.get('TG_USER_ID', 'string'), // tg 机器人的 TG_USER_ID 22 | }, 23 | logname: shname.substring(shname.indexOf('_') + 1, shname.lastIndexOf('.')), 24 | from: 'task', 25 | }); 26 | -------------------------------------------------------------------------------- /ck_acfun.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 31 7 * * * 4 | new Env('AcFun'); 5 | """ 6 | 7 | import re 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class AcFun: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | self.contentid = "27259341" 19 | self.st = "" 20 | 21 | @staticmethod 22 | def login(phone, password, session): 23 | url = "https://id.app.acfun.cn/rest/web/login/signin" 24 | body = f"username={phone}&password={password}&key=&captcha=" 25 | res = session.post(url=url, data=body).json() 26 | return (True, res) if res.get("result") == 0 else (False, res.get("err_msg")) 27 | 28 | def get_video(self, session): 29 | url = "https://www.acfun.cn/rest/pc-direct/rank/channel" 30 | res = session.get(url=url).json() 31 | self.contentid = res.get("rankList")[0].get("contentId") 32 | 33 | @staticmethod 34 | def sign(session): 35 | url = "https://www.acfun.cn/rest/pc-direct/user/signIn" 36 | res = session.post(url=url).json() 37 | return res.get("msg") 38 | 39 | def danmu(self, session): 40 | url = "https://www.acfun.cn/rest/pc-direct/new-danmaku/add" 41 | data = { 42 | "mode": "1", 43 | "color": "16777215", 44 | "size": "25", 45 | "body": "123321", 46 | "videoId": "26113662", 47 | "position": "2719", 48 | "type": "douga", 49 | "id": "31224739", 50 | "subChannelId": "1", 51 | "subChannelName": "动画", 52 | } 53 | response = session.get(url=f"https://www.acfun.cn/v/ac{self.contentid}") 54 | videoId = re.findall(r'"currentVideoId":(\d+),', response.text) 55 | subChannel = re.findall( 56 | r'{subChannelId:(\d+),subChannelName:"([\u4e00-\u9fa5]+)"}', response.text 57 | ) 58 | if videoId: 59 | data["videoId"] = videoId[0] 60 | data["subChannelId"] = subChannel[0][0] 61 | data["subChannelName"] = subChannel[0][1] 62 | res = session.post(url=url, data=data).json() 63 | return "弹幕成功" if res.get("result") == 0 else "弹幕失败" 64 | 65 | def get_token(self, session): 66 | url = "https://id.app.acfun.cn/rest/web/token/get?sid=acfun.midground.api" 67 | res = session.post(url=url).json() 68 | self.st = res.get("acfun.midground.api_st") if res.get("result") == 0 else "" 69 | 70 | def like(self, session): 71 | like_url = "https://kuaishouzt.com/rest/zt/interact/add" 72 | unlike_url = "https://kuaishouzt.com/rest/zt/interact/delete" 73 | body = ( 74 | f"kpn=ACFUN_APP&kpf=PC_WEB&subBiz=mainApp&interactType=1&" 75 | f"objectType=2&objectId={self.contentid}&acfun.midground.api_st={self.st}&" 76 | f"extParams%5BisPlaying%5D=false&extParams%5BshowCount%5D=1&extParams%5B" 77 | f"otherBtnClickedCount%5D=10&extParams%5BplayBtnClickedCount%5D=0" 78 | ) 79 | res = session.post(url=like_url, data=body).json() 80 | session.post(url=unlike_url, data=body) 81 | return "点赞成功" if res.get("result") == 1 else "点赞失败" 82 | 83 | def throwbanana(self, session): 84 | url = "https://www.acfun.cn/rest/pc-direct/banana/throwBanana" 85 | data = {"resourceId": self.contentid, "count": "1", "resourceType": "2"} 86 | res = session.post(url=url, data=data).json() 87 | return "投🍌成功" if res.get("result") == 0 else "投🍌失败" 88 | 89 | @staticmethod 90 | def get_info(session): 91 | url = "https://www.acfun.cn/rest/pc-direct/user/personalInfo" 92 | res = session.get(url=url).json() 93 | if res.get("result") != 0: 94 | return "查询失败" 95 | info = res.get("info") 96 | return f'当前等级: {info.get("level")}\n持有香蕉: {info.get("banana")}' 97 | 98 | def main(self): 99 | msg_all = "" 100 | for check_item in self.check_items: 101 | phone = check_item.get("phone") 102 | password = check_item.get("password") 103 | 104 | s = requests.session() 105 | s.headers.update( 106 | { 107 | "accept": "*/*", 108 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", 109 | "content-type": "application/x-www-form-urlencoded; charset=UTF-8", 110 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 111 | "AppleWebKit/537.36 (KHTML, like Gecko) " 112 | "Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70", 113 | "Referer": "https://www.acfun.cn/", 114 | } 115 | ) 116 | 117 | flag, res = self.login(phone, password, s) 118 | 119 | if flag is True: 120 | self.get_video(s) 121 | self.get_token(s) 122 | 123 | msg = ( 124 | f"帐号信息: *******{phone[-4:]}\n" 125 | f"签到状态: {self.sign(s)}\n" 126 | f"点赞任务: {self.like(s)}\n" 127 | f"弹幕任务: {self.danmu(s)}\n" 128 | f"香蕉任务: {self.throwbanana(s)}\n" 129 | f"{self.get_info(s)}" 130 | ) 131 | 132 | else: 133 | msg = f"*******{phone[-4:]} {res}" 134 | 135 | msg_all += msg + "\n\n" 136 | return msg_all 137 | 138 | 139 | if __name__ == "__main__": 140 | _data = get_data() 141 | _check_items = _data.get("ACFUN", []) 142 | result = AcFun(check_items=_check_items).main() 143 | send("AcFun", result) 144 | -------------------------------------------------------------------------------- /ck_airport.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :author @Icrons 4 | cron: 20 10 * * * 5 | new Env('机场签到'); 6 | """ 7 | 8 | import json 9 | import re 10 | import traceback 11 | 12 | import requests 13 | import urllib3 14 | 15 | from notify_mtr import send 16 | from utils import get_data 17 | 18 | urllib3.disable_warnings() 19 | 20 | 21 | class SspanelQd: 22 | def __init__(self, check_items): 23 | self.check_items = check_items 24 | 25 | @staticmethod 26 | def checkin(url, email, password): 27 | url = url.rstrip("/") 28 | emails = email.split("@") 29 | email = f"{emails[0]}%40{emails[1]}" if len(emails) > 1 else emails[0] 30 | session = requests.session() 31 | 32 | # 以下 except 都是用来捕获当 requests 请求出现异常时, 33 | # 通过捕获然后等待网络情况的变化,以此来保护程序的不间断运行 34 | try: 35 | session.get(url, verify=False) 36 | except requests.exceptions.ConnectionError: 37 | return f"{url}\n网络不通" 38 | except requests.exceptions.ChunkedEncodingError: 39 | return f"{url}\n分块编码错误" 40 | except Exception: 41 | print(f"未知错误,错误信息:\n{traceback.format_exc()}") 42 | return f"{url}\n未知错误,请查看日志" 43 | 44 | login_url = f"{url}/auth/login" 45 | headers = { 46 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) " 47 | "AppleWebKit/537.36 (KHTML, like Gecko) " 48 | "Chrome/56.0.2924.87 Safari/537.36", 49 | "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 50 | } 51 | login_data = f"email={email}&passwd={password}&code=".encode() 52 | 53 | try: 54 | response = session.post( 55 | login_url, login_data, headers=headers, verify=False 56 | ) 57 | login_text = response.text.encode("utf-8").decode("unicode_escape") 58 | print(f"{url} 接口登录返回信息:{login_text}") 59 | login_json = json.loads(login_text) 60 | if login_json.get("ret") == 0: 61 | return f'{url}\n{login_json.get("msg")}' 62 | except Exception: 63 | print(f"登录失败,错误信息:\n{traceback.format_exc()}") 64 | return f"{url}\n登录失败,请查看日志" 65 | 66 | headers = { 67 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) " 68 | "AppleWebKit/537.36 (KHTML, like Gecko) " 69 | "Chrome/56.0.2924.87 Safari/537.36", 70 | "Referer": f"{url}/user", 71 | } 72 | 73 | try: 74 | response = session.post( 75 | f"{url}/user/checkin", headers=headers, verify=False 76 | ) 77 | sign_text = response.text.encode("utf-8").decode("unicode_escape") 78 | print(f"{url} 接口签到返回信息:{sign_text}") 79 | sign_json = json.loads(sign_text) 80 | sign_msg = sign_json.get("msg") 81 | msg = f"{url}\n{sign_msg}" if sign_msg else f"{url}\n{sign_json}" 82 | except Exception: 83 | msg = f"{url}\n签到失败失败,请查看日志" 84 | print(f"签到失败,错误信息:\n{traceback.format_exc()}") 85 | 86 | # 以下只适配了editXY主题 87 | try: 88 | info_url = f"{url}/user" 89 | response = session.get(info_url, verify=False) 90 | level = re.findall(r'\["Class", "(.*?)"],', response.text)[0] 91 | day = re.findall(r'\["Class_Expire", "(.*)"],', response.text)[0] 92 | rest = re.findall(r'\["Unused_Traffic", "(.*?)"]', response.text)[0] 93 | return ( 94 | f"{url}\n" 95 | f"- 今日签到信息:{msg}\n" 96 | f"- 用户等级:{level}\n" 97 | f"- 到期时间:{day}\n" 98 | f"- 剩余流量:{rest}" 99 | ) 100 | except Exception: 101 | return msg 102 | 103 | def main(self): 104 | msg_all = "" 105 | for check_item in self.check_items: 106 | # 机场地址 107 | url = str(check_item.get("url")) 108 | # 登录信息 109 | email = str(check_item.get("email")) 110 | password = str(check_item.get("password")) 111 | if url and email and password: 112 | msg = self.checkin(url, email, password) 113 | else: 114 | msg = "配置错误" 115 | msg_all += msg + "\n\n" 116 | return msg_all 117 | 118 | 119 | if __name__ == "__main__": 120 | _data = get_data() 121 | _check_items = _data.get("AIRPORT", []) 122 | res = SspanelQd(check_items=_check_items).main() 123 | send("机场签到", res) 124 | -------------------------------------------------------------------------------- /ck_baidu_url_submit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 32 7 * * * 4 | new Env('百度搜索资源平台'); 5 | """ 6 | 7 | from urllib.parse import parse_qs, urlsplit 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class BaiduUrlSubmit: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def url_submit(data_url: str, submit_url: str, times: int = 100) -> str: 21 | site = parse_qs(urlsplit(submit_url).query).get("site", [])[0] 22 | urls_data = requests.get(data_url) 23 | remain = 100000 24 | success_count = 0 25 | error_count = 0 26 | for _ in range(times): 27 | try: 28 | res = requests.post(submit_url, data=urls_data).json() 29 | if res.get("success"): 30 | remain = res.get("remain") 31 | success_count += res.get("success") 32 | else: 33 | error_count += 1 34 | except Exception as e: 35 | print(e) 36 | error_count += 1 37 | return ( 38 | f"站点地址: {site}\n" 39 | f"当天剩余的可推送 url 条数: {remain}\n" 40 | f"成功推送的 url 条数: {success_count}\n" 41 | f"成功推送的 url 次数: {times - error_count}\n" 42 | f"失败推送的 url 次数: {error_count}" 43 | ) 44 | 45 | def main(self): 46 | msg_all = "" 47 | for check_item in self.check_items: 48 | data_url = check_item.get("data_url") 49 | submit_url = check_item.get("submit_url") 50 | times = int(check_item.get("times", 100)) 51 | if data_url and submit_url: 52 | msg = self.url_submit(data_url, submit_url, times) 53 | else: 54 | msg = "配置错误" 55 | msg_all += msg + "\n\n" 56 | return msg_all 57 | 58 | 59 | if __name__ == "__main__": 60 | _data = get_data() 61 | _check_items = _data.get("BAIDU", []) 62 | result = BaiduUrlSubmit(check_items=_check_items).main() 63 | send("百度搜索资源平台", result) 64 | -------------------------------------------------------------------------------- /ck_bilibili_helper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # shellcheck disable=SC2188 4 | <<'COMMENT' 5 | cron: 59 17 * * * 6 | new Env('Bilibili 助手'); 7 | COMMENT 8 | 9 | if [ -f "$(dirname "$0")/utils_env.sh" ]; then 10 | source "$(dirname "$0")/utils_env.sh" 11 | else 12 | wget -q -O utils_env.sh https://ghproxy.com/https://raw.githubusercontent.com/OreosLab/checkinpanel/master/utils_env.sh 13 | source "$(dirname "$0")/utils_env.sh" 14 | fi 15 | get_some_path 16 | check_jq_installed_status 17 | check_java_installed_status 18 | 19 | conf_file="${CONF_PATH}/java_conf.json" 20 | bili_path="${SCR_PATH}/bilibili" 21 | 22 | if [ ! -d "${bili_path}" ]; then 23 | mkdir -p "${bili_path}" 24 | fi 25 | 26 | cd "${bili_path}" || exit 27 | if [ -f "/tmp/bili-helper.log" ]; then 28 | VERSION=$(grep "当前版本" "/tmp/bili-helper.log" | awk '{print $2}') 29 | else 30 | VERSION="0" 31 | fi 32 | echo "当前版本:"$VERSION 33 | 34 | latest=$(curl -s https://api.github.com/repos/OreosLab/bili/releases/latest) 35 | latest_VERSION=$(echo "$latest" | jq '.tag_name' | sed 's/v\|"//g') 36 | echo "最新版本:""$latest_VERSION" 37 | download_url=$(echo "$latest" | jq '.assets[0].browser_download_url' | sed 's/"//g') 38 | download() { 39 | curl -L -o "./BILIBILI-HELPER.zip" "https://ghproxy.com/$download_url" 40 | mkdir ./tmp 41 | echo "正在解压文件......." 42 | unzip -o -d ./tmp/ BILIBILI-HELPER.zip 43 | cp -f ./tmp/BILIBILI-HELPER*.jar BILIBILI-HELPER.jar 44 | if [ ! -f "${conf_file}" ]; then 45 | echo "配置文件不存在。" 46 | cp -f ./tmp/config.json "${conf_file}" 47 | fi 48 | echo "清除缓存........." 49 | rm -rf tmp 50 | rm -rf BILIBILI-HELPER.zip 51 | echo "更新完成" 52 | } 53 | 54 | function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; } 55 | if version_lt $VERSION "$latest_VERSION"; then 56 | echo "有新版本,开始更新" 57 | download 58 | else 59 | echo "已经是最新版本,不需要更新!!!" 60 | fi 61 | if [ ! -f "${bili_path}/BILIBILI-HELPER.jar" ]; then 62 | echo "没找到BILIBILI-HELPER.jar,开始下载.........." 63 | download 64 | fi 65 | 66 | echo "配置文件路径:""$conf_file" 67 | java -jar BILIBILI-HELPER.jar "$conf_file" 68 | -------------------------------------------------------------------------------- /ck_ccava.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 55 18 * * * 4 | new Env('CCAVA'); 5 | """ 6 | 7 | import requests 8 | 9 | from notify_mtr import send 10 | from utils import get_data 11 | 12 | 13 | class CCAVA: 14 | def __init__(self, check_items): 15 | self.check_items = check_items 16 | 17 | @staticmethod 18 | def sign(cookie): 19 | url = "https://pc.ccava.net/zb_users/plugin/mochu_us/cmd.php?act=qiandao" 20 | res = requests.get(url, headers={"Cookie": cookie}).json() 21 | if "登录" in res["msg"]: 22 | return "cookie 失效" 23 | if "今天" in res["msg"]: 24 | return f'重复签到,剩余 {res["giod"]} 月光币' 25 | return f'签到成功,剩余 {res["giod"]} 月光币' 26 | 27 | def main(self): 28 | msg_all = "" 29 | for check_item in self.check_items: 30 | cookie = check_item.get("cookie") 31 | msg = self.sign(cookie) 32 | msg_all += msg + "\n\n" 33 | return msg_all 34 | 35 | 36 | if __name__ == "__main__": 37 | _data = get_data() 38 | _check_items = _data.get("CCAVA", []) 39 | result = CCAVA(check_items=_check_items).main() 40 | send("CCAVA", result) 41 | -------------------------------------------------------------------------------- /ck_cloud189.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :source https://github.com/MayoBlueSky/My-Actions/blob/master/function/cloud189/checkin.py 4 | cron: 30 9 * * * 5 | new Env('天翼云盘'); 6 | """ 7 | 8 | import base64 9 | import re 10 | import time 11 | 12 | import requests 13 | import rsa 14 | 15 | from notify_mtr import send 16 | from utils import get_data 17 | 18 | 19 | class Cloud189: 20 | def __init__(self, check_items): 21 | self.check_items = check_items 22 | self.b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 23 | 24 | @staticmethod 25 | def int2char(a): 26 | return list("0123456789abcdefghijklmnopqrstuvwxyz")[a] 27 | 28 | def b64tohex(self, a): 29 | d = "" 30 | e = 0 31 | c = 0 32 | for i in range(len(a)): 33 | if list(a)[i] != "=": 34 | v = self.b64map.index(list(a)[i]) 35 | if e == 0: 36 | e = 1 37 | d += self.int2char(v >> 2) 38 | c = 3 & v 39 | elif e == 1: 40 | e = 2 41 | d += self.int2char(c << 2 | v >> 4) 42 | c = 15 & v 43 | elif e == 2: 44 | e = 3 45 | d += self.int2char(c) 46 | d += self.int2char(v >> 2) 47 | c = 3 & v 48 | else: 49 | e = 0 50 | d += self.int2char(c << 2 | v >> 4) 51 | d += self.int2char(15 & v) 52 | if e == 1: 53 | d += self.int2char(c << 2) 54 | return d 55 | 56 | def rsa_encode(self, j_rsakey, string): 57 | rsa_key = f"-----BEGIN PUBLIC KEY-----\n{j_rsakey}\n-----END PUBLIC KEY-----" 58 | pubkey = rsa.PublicKey.load_pkcs1_openssl_pem(rsa_key.encode()) 59 | return self.b64tohex( 60 | (base64.b64encode(rsa.encrypt(f"{string}".encode(), pubkey))).decode() 61 | ) 62 | 63 | def login(self, session, username, password): 64 | url = ( 65 | "https://cloud.189.cn/api/portal/loginUrl.action?" 66 | "redirectURL=https://cloud.189.cn/web/redirect.html" 67 | ) 68 | res = session.get(url).text 69 | captchatoken = re.findall(r"captchaToken' value='(.+?)'", res)[0] 70 | lt = re.findall(r'lt = "(.+?)"', res)[0] 71 | returnurl = re.findall(r"returnUrl = '(.+?)'", res)[0] 72 | paramid = re.findall(r'paramId = "(.+?)"', res)[0] 73 | j_rsakey = re.findall(r'j_rsaKey" value="(\S+)"', res, re.M)[0] 74 | 75 | session.headers.update({"lt": lt}) 76 | username = self.rsa_encode(j_rsakey, username) 77 | password = self.rsa_encode(j_rsakey, password) 78 | url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do" 79 | headers = { 80 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) " 81 | "Gecko/20100101 Firefox/76.0", 82 | "Referer": "https://open.e.189.cn/", 83 | } 84 | data = { 85 | "appKey": "cloud", 86 | "accountType": "01", 87 | "userName": f"{{RSA}}{username}", 88 | "password": f"{{RSA}}{password}", 89 | "validateCode": "", 90 | "captchaToken": captchatoken, 91 | "returnUrl": returnurl, 92 | "mailSuffix": "@189.cn", 93 | "paramId": paramid, 94 | } 95 | res = session.post(url, data=data, headers=headers, timeout=5).json() 96 | if res["result"] != 0: 97 | return "登陆状态: " + res["msg"] 98 | redirect_url = res["toUrl"] 99 | session.get(redirect_url) 100 | return True 101 | 102 | @staticmethod 103 | def sign(session): 104 | rand = round(time.time() * 1000) 105 | surl = ( 106 | f"https://api.cloud.189.cn/mkt/userSign.action?" 107 | f"rand={rand}&clientType=TELEANDROID&version=8.6.3&model=SM-G930K" 108 | ) 109 | url = ( 110 | "https://m.cloud.189.cn/v2/drawPrizeMarketDetails.action?" 111 | "taskId=TASK_SIGNIN&activityId=ACT_SIGNIN" 112 | ) 113 | url2 = ( 114 | "https://m.cloud.189.cn/v2/drawPrizeMarketDetails.action?" 115 | "taskId=TASK_SIGNIN_PHOTOS&activityId=ACT_SIGNIN" 116 | ) 117 | headers = { 118 | "User-Agent": "Mozilla/5.0 (Linux; Android 5.1.1; SM-G930K Build/NRD90M; wv) " 119 | "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 " 120 | "Chrome/74.0.3729.136 Mobile Safari/537.36 Ecloud/8.6.3 Android/22 " 121 | "clientId/355325117317828 clientModel/SM-G930K " 122 | "imsi/460071114317824 clientChannelId/qq proVersion/1.0.6", 123 | "Referer": "https://m.cloud.189.cn/zhuanti/2016/sign/index.jsp?albumBackupOpened=1", 124 | "Host": "m.cloud.189.cn", 125 | "Accept-Encoding": "gzip, deflate", 126 | } 127 | response = session.get(surl, headers=headers) 128 | netdiskbonus = response.json().get("netdiskBonus") 129 | if response.json().get("isSign") == "false": 130 | msg = f"签到结果: 未签到,签到获得 {netdiskbonus}M 空间" 131 | else: 132 | msg = f"签到结果: 已经签到过了,签到获得 {netdiskbonus}M 空间" 133 | 134 | response = session.get(url, headers=headers) 135 | if "errorCode" in response.text: 136 | msg += f"\n第一次抽奖: {response.json().get('errorCode')}" 137 | else: 138 | description = response.json().get("description", "") 139 | if description in ["1", 1]: 140 | description = "50M 空间" 141 | msg += f"\n第一次抽奖: 获得 {description}" 142 | 143 | response = session.get(url2, headers=headers) 144 | if "errorCode" in response.text: 145 | msg += f"\n第二次抽奖: {response.json().get('errorCode')}" 146 | else: 147 | description = response.json().get("description", "") 148 | if description in ["1", 1]: 149 | description = "50M 空间" 150 | msg += f"\n第二次抽奖: 获得 {description}" 151 | return msg 152 | 153 | def main(self): 154 | msg_all = "" 155 | for check_item in self.check_items: 156 | phone = check_item.get("phone") 157 | password = check_item.get("password") 158 | session = requests.Session() 159 | flag = self.login(session, phone, password) 160 | sign_msg = self.sign(session) if flag is True else flag 161 | msg = f"帐号信息: *******{phone[-4:]}\n{sign_msg}" 162 | msg_all += msg + "\n\n" 163 | return msg_all 164 | 165 | 166 | if __name__ == "__main__": 167 | _data = get_data() 168 | _check_items = _data.get("CLOUD189", []) 169 | result = Cloud189(check_items=_check_items).main() 170 | send("天翼云盘", result) 171 | -------------------------------------------------------------------------------- /ck_csdn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 30 10 * * * 4 | new Env('CSDN'); 5 | """ 6 | 7 | import requests 8 | 9 | from notify_mtr import send 10 | from utils import get_data 11 | 12 | 13 | class CSDN: 14 | def __init__(self, check_items): 15 | self.check_items = check_items 16 | self.headers = { 17 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) " 18 | "AppleWebKit/537.36 (KHTML, like Gecko)" 19 | "Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74" 20 | } 21 | 22 | def sign(self, cookies): 23 | url = "https://me.csdn.net/api/LuckyDraw_v2/signIn" 24 | res = requests.get(url, headers=self.headers, cookies=cookies).json() 25 | if res.get("code") == 200: 26 | msg = res.get("data").get("msg") 27 | else: 28 | msg = "签到失败" 29 | print(res) 30 | return msg 31 | 32 | def draw(self, cookies): 33 | url = "https://me.csdn.net/api/LuckyDraw_v2/goodluck" 34 | res = requests.get(url, headers=self.headers, cookies=cookies).json() 35 | if res.get("code") == 200: 36 | return ( 37 | f", {res.get('data').get('prize_title')}" 38 | if res.get("data").get("prize_title") is not None 39 | else f"{res.get('data').get('msg')}" 40 | ) 41 | return "抽奖失败" 42 | 43 | def main(self): 44 | msg_all = "" 45 | for check_item in self.check_items: 46 | cookie = { 47 | item.split("=")[0]: item.split("=")[1] 48 | for item in check_item.get("cookie").split("; ") 49 | } 50 | try: 51 | user_name = cookie.get("UserName", "") 52 | except Exception as e: 53 | print(f"获取用户信息失败: {e}") 54 | user_name = "未获取到用户信息" 55 | sign_msg = self.sign(cookie) 56 | draw_msg = self.draw(cookie) 57 | msg = f"帐号信息: {user_name}\n签到信息: {sign_msg}\n抽奖结果: {draw_msg}" 58 | msg_all += msg + "\n\n" 59 | return msg_all 60 | 61 | 62 | if __name__ == "__main__": 63 | _data = get_data() 64 | _check_items = _data.get("CSDN", []) 65 | result = CSDN(check_items=_check_items).main() 66 | send("CSDN", result) 67 | -------------------------------------------------------------------------------- /ck_du163.js: -------------------------------------------------------------------------------- 1 | /* 2 | 52 9 * * * ck_du163.js 3 | */ 4 | 5 | const axios = require('axios'); 6 | 7 | const utils = require('./utils'); 8 | const Env = utils.Env; 9 | const getData = utils.getData; 10 | 11 | const $ = new Env('网易蜗牛读书'); 12 | const notify = $.isNode() ? require('./notify') : ''; 13 | const COOKIES_DU163 = getData().DU163; 14 | 15 | var desp = ''; 16 | 17 | du163(); 18 | 19 | function du163() { 20 | return new Promise(async (resolve) => { 21 | let result = '【网易蜗牛读书】: '; 22 | if (COOKIES_DU163) { 23 | Log('cookie 数量:' + COOKIES_DU163.length); 24 | for (let a = 0; a < COOKIES_DU163.length; a++) { 25 | let cookie = COOKIES_DU163[a].cookie; 26 | let _xsrf = cookie.match(/_xsrf=(\S*);*/)[1]; 27 | let user_agent = COOKIES_DU163[a].user_agent; 28 | Log('\n======== [Cookie ' + (a + 1) + '] Start ======== '); 29 | try { 30 | const headers = { 31 | headers: { 32 | cookie: cookie, 33 | 'user-agent': user_agent, 34 | }, 35 | }; 36 | let url = 'https://du.163.com/activity/201907/activityCenter/sign.json'; 37 | let data = `csrfToken=${_xsrf}`; 38 | let res = await axios.post(url, data, headers); 39 | if (res.data.code == -1104) { 40 | var msg = res.data.msg; 41 | } else if (res.data.code == 0) { 42 | msg = res.data.message + ' 连签' + res.data.continuousSignedDays + '天'; 43 | } else { 44 | msg = '签到失败 ' + res.data.msg; 45 | } 46 | result += msg; 47 | } catch (err) { 48 | result = result + '签到失败 ' + err.response.data.msg; 49 | } 50 | } 51 | } else { 52 | result += '请填写网易蜗牛读书cookies'; 53 | } 54 | resolve(result); 55 | Log(result); 56 | notify.sendNotify('网易蜗牛读书', desp); 57 | }); 58 | } 59 | 60 | function Log(info) { 61 | console.log(info); 62 | desp = desp + info + '\n'; 63 | return desp; 64 | } 65 | 66 | module.exports = du163; 67 | -------------------------------------------------------------------------------- /ck_enshan.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 1 15 * * * 4 | new Env('恩山论坛'); 5 | """ 6 | 7 | import re 8 | import time 9 | 10 | import requests 11 | 12 | from notify_mtr import send 13 | from utils import get_data 14 | 15 | 16 | class Enshan: 17 | def __init__(self, check_items): 18 | self.check_items = check_items 19 | 20 | @staticmethod 21 | def sign(cookie): 22 | url = ( 23 | "https://www.right.com.cn/FORUM/home.php?mod=spacecp&ac=credit&showcredit=1" 24 | ) 25 | headers = { 26 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 27 | "AppleWebKit/537.36 (KHTML, like Gecko) " 28 | "Chrome/92.0.4515.131 Safari/537.36", 29 | "Cookie": cookie, 30 | } 31 | session = requests.session() 32 | response = session.get(url, headers=headers) 33 | try: 34 | coin = re.findall("恩山币: (.*?)nb  ", response.text)[0] 35 | point = re.findall("积分: (.*?)(.*?)[^<]+[^<]+.*?', 29 | re.I, 30 | ) 31 | 32 | # 登录状态正则 33 | login_status_ptn = re.compile('Logout', re.I) 34 | 35 | 36 | class FreeNom: 37 | def __init__(self, check_items: list): 38 | self.check_items = check_items 39 | self._s = requests.Session() 40 | self._s.headers.update( 41 | { 42 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/79.0.3945.130 Safari/537.36" 43 | } 44 | ) 45 | 46 | def _login(self, usr: str, pwd: str) -> bool: 47 | self._s.headers.update( 48 | { 49 | "content-type": "application/x-www-form-urlencoded", 50 | "referer": "https://my.freenom.com/clientarea.php", 51 | } 52 | ) 53 | r = self._s.post(LOGIN_URL, data={"username": usr, "password": pwd}) 54 | return r.status_code == 200 55 | 56 | def main(self) -> str: 57 | msg = "" 58 | msg_all = "" 59 | 60 | for i, check_item in enumerate(self.check_items, start=1): 61 | username = check_item.get("username") 62 | password = check_item.get("password") 63 | 64 | # login 65 | if not self._login(usr=username, pwd=password): 66 | msg_all += f"account{i} login failed\n\n" 67 | continue 68 | 69 | # check domain status 70 | self._s.headers.update({"referer": "https://my.freenom.com/clientarea.php"}) 71 | r = self._s.get(DOMAIN_STATUS_URL) 72 | 73 | # login status check 74 | if not re.search(login_status_ptn, r.text): 75 | msg_all += f"account{i} get login status failed\n\n" 76 | continue 77 | 78 | # page token 79 | match = re.search(token_ptn, r.text) 80 | if not match: 81 | msg_all += f"account{i} get page token failed\n\n" 82 | continue 83 | token = match[1] 84 | 85 | # domains 86 | domains = re.findall(domain_info_ptn, r.text) 87 | 88 | # renew domains 89 | res = "" 90 | for domain, days, renewal_id in domains: 91 | if int(days) < 14: 92 | self._s.headers.update( 93 | { 94 | "referer": f"https://my.freenom.com/domains.php?a=renewdomain&domain={renewal_id}", 95 | "content-type": "application/x-www-form-urlencoded", 96 | } 97 | ) 98 | r = self._s.post( 99 | RENEW_DOMAIN_URL, 100 | data={ 101 | "token": token, 102 | "renewalid": renewal_id, 103 | f"renewalperiod[{renewal_id}]": "12M", 104 | "paymentmethod": "credit", 105 | }, 106 | ) 107 | res += ( 108 | f"{domain} 续期成功\n" 109 | if r.text.find("Order Confirmation") != -1 110 | else f"{domain} 续期失败" 111 | ) 112 | res += f"{domain} 还有 {days} 天续期\n" 113 | msg = f"账号{i}\n{res}" 114 | msg_all += msg + "\n" 115 | return msg_all 116 | 117 | 118 | if __name__ == "__main__": 119 | _data = get_data() 120 | _check_items = _data.get("FREENOM", []) 121 | result = FreeNom(check_items=_check_items).main() 122 | send("FreeNom", result) 123 | -------------------------------------------------------------------------------- /ck_game163.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 20 8 * * * 4 | new Env('网易云游戏'); 5 | """ 6 | 7 | import requests 8 | 9 | from notify_mtr import send 10 | from utils import get_data 11 | 12 | 13 | class Game163: 14 | def __init__(self, check_items): 15 | self.check_items = check_items 16 | 17 | @staticmethod 18 | def checkin(authorization): 19 | url = "http://n.cg.163.com/api/v2/sign-today" 20 | headers = { 21 | "user-agent": "Mozilla/5.0 (Linux; Android 10; Redmi K30 Build/QKQ1.190825.002; wv) " 22 | "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 " 23 | "Chrome/85.0.4183.127 Mobile Safari/537.36", 24 | "authorization": authorization, 25 | } 26 | res = requests.post(url, headers=headers).text 27 | return "cookie 已失效" if res[0] == "{" else "签到成功" 28 | 29 | def main(self): 30 | msg_all = "" 31 | for check_item in self.check_items: 32 | authorization = str(check_item.get("authorization")) 33 | msg = self.checkin(authorization) 34 | msg_all += msg + "\n\n" 35 | return msg_all 36 | 37 | 38 | if __name__ == "__main__": 39 | _data = get_data() 40 | _check_items = _data.get("GAME163", []) 41 | result = Game163(check_items=_check_items).main() 42 | send("网易云游戏", result) 43 | -------------------------------------------------------------------------------- /ck_glados.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :author @XFY9326 4 | cron: 11 6 * * * 5 | new Env('GLaDOS'); 6 | """ 7 | 8 | import traceback 9 | from typing import Optional 10 | 11 | import requests 12 | 13 | import utils_tmp 14 | from notify_mtr import send 15 | from utils import get_data 16 | 17 | 18 | class GLaDOS: 19 | def __init__(self, check_items: list): 20 | self.check_items = check_items 21 | self.original_url = "https://glados.rocks" 22 | self.UA = ( 23 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " 24 | "AppleWebKit/537.36 (KHTML, like Gecko) " 25 | "Chrome/91.0.4472.114 Safari/537.36" 26 | ) 27 | 28 | def api_traffic(self, cookies: str): 29 | traffic_url = f"{self.original_url}/api/user/traffic" 30 | referer_url = f"{self.original_url}/console" 31 | 32 | with requests.get( 33 | traffic_url, 34 | headers={ 35 | "cookie": cookies, 36 | "referer": referer_url, 37 | "origin": self.original_url, 38 | "user-agent": self.UA, 39 | "content-type": "application/json;charset=UTF-8", 40 | }, 41 | ) as r: 42 | return r.json() 43 | 44 | def api_check_in(self, cookies: str) -> dict: 45 | check_in_url = f"{self.original_url}/api/user/checkin" 46 | referer_url = f"{self.original_url}/console/checkin" 47 | 48 | payload = {"token": "glados.network"} 49 | 50 | with requests.post( 51 | check_in_url, 52 | headers={ 53 | "cookie": cookies, 54 | "referer": referer_url, 55 | "origin": self.original_url, 56 | "user-agent": self.UA, 57 | "content-type": "application/json;charset=UTF-8", 58 | }, 59 | json=payload, 60 | ) as r: 61 | return r.json() 62 | 63 | def api_status(self, cookies: str) -> dict: 64 | status_url = f"{self.original_url}/api/user/status" 65 | referer_url = f"{self.original_url}/console/checkin" 66 | 67 | with requests.get( 68 | status_url, 69 | headers={ 70 | "cookie": cookies, 71 | "referer": referer_url, 72 | "origin": self.original_url, 73 | "user-agent": self.UA, 74 | }, 75 | ) as r: 76 | return r.json() 77 | 78 | @staticmethod 79 | def get_budget(vip_level: Optional[int]) -> dict: 80 | budget_info = utils_tmp.budget_list 81 | user_budgets = [ 82 | i 83 | for i in budget_info 84 | if (vip_level is not None and i.get("vip") == vip_level) 85 | or (vip_level is None and "vip" not in i) 86 | ] 87 | if user_budgets: 88 | return user_budgets[0] 89 | raise OSError(f"Budget info not found for this user! VIP: {vip_level}") 90 | 91 | def main(self): 92 | msg_all = "" 93 | for check_item in self.check_items: 94 | cookie = check_item.get("cookie") 95 | try: 96 | check_in_res = self.api_check_in(cookie) 97 | check_in_msg = check_in_res["message"] 98 | if check_in_msg == "\u6ca1\u6709\u6743\u9650": 99 | msg = ( 100 | "--------------------\n" 101 | "Msg: Your cookies are expired!\n" 102 | "--------------------" 103 | ) 104 | msg_all += msg 105 | continue 106 | status_res = self.api_status(cookie) 107 | # print(status_res) 108 | left_days = int(str(status_res["data"]["leftDays"]).split(".")[0]) 109 | vip_level = status_res["data"]["vip"] 110 | traffic_res = self.api_traffic(cookie) 111 | used_gb = traffic_res["data"]["today"] / 1024 / 1024 / 1024 112 | user_budget = self.get_budget(vip_level) 113 | total_gb = user_budget["budget"] 114 | plan = user_budget["level"] 115 | msg = ( 116 | "--------------------\n" 117 | f"Msg: {check_in_msg}\n" 118 | f"Plan: {plan} Plan\n" 119 | f"Left days: {left_days}\n" 120 | f"Usage: {used_gb:.3f} GB\n" 121 | f"Total: {total_gb} GB\n" 122 | "--------------------" 123 | ) 124 | except Exception: 125 | msg = ( 126 | "--------------------\n" 127 | "Msg: Check in error!\n" 128 | "Error:\n" 129 | f"{traceback.format_exc()}\n" 130 | "--------------------" 131 | ) 132 | msg_all += msg + "\n\n" 133 | return msg_all 134 | 135 | 136 | if __name__ == "__main__": 137 | _data = get_data() 138 | _check_items = _data.get("GLADOS", []) 139 | result = GLaDOS(check_items=_check_items).main() 140 | send("GLaDOS", result) 141 | -------------------------------------------------------------------------------- /ck_haidilao.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 20 8 * * * 4 | new Env('海底捞会员签到'); 5 | """ 6 | 7 | import json 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class Haidilao: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def checkin(openid, uid): 21 | headers = { 22 | "Host": "superapp-public.kiwa-tech.com", 23 | "Content-Length": "115", 24 | "appId": "15", 25 | "content-type": "application/json", 26 | "Accept-Encoding": "gzip,compress,br,deflate", 27 | "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X) " 28 | "AppleWebKit/605.1.15 (KHTML, like Gecko) " 29 | "Mobile MicroMessenger NetType/4G Language/en miniProgram", 30 | "Referer": "https://servicewechat.com/wx1ddeb67115f30d1a/14/page-frame.html", 31 | } 32 | login_data = { 33 | "openId": openid, 34 | "country": "CN", 35 | "uid": uid, 36 | "type": 1, 37 | "codeType": 1, 38 | } 39 | url = "https://superapp-public.kiwa-tech.com/" 40 | 41 | try: 42 | login_res = requests.post( 43 | f"{url}login/thirdCommLogin", headers=headers, json=login_data 44 | ).json() 45 | if not login_res["success"]: 46 | return "登录失败" 47 | data = login_res["data"] 48 | except json.decoder.JSONDecodeError: 49 | return "登录请求失败" 50 | 51 | headers["_HAIDILAO_APP_TOKEN"] = data["token"] 52 | headers["ReqType"] = "APPH5" 53 | headers[ 54 | "Referer" 55 | ] = f'{url}app-sign-in/?SignInToken={data["token"]}&source=MiniApp' 56 | 57 | try: 58 | signin_res = requests.post( 59 | f"{url}activity/wxapp/signin/signin", 60 | headers=headers, 61 | json={"signinSource": "MiniApp"}, 62 | ).json() 63 | if "请勿重复操作" in signin_res["msg"]: 64 | return "今日签到过了" 65 | except json.decoder.JSONDecodeError: 66 | return "签到请求失败" 67 | 68 | try: 69 | fragment_res = requests.post( 70 | f"{url}activity/wxapp/signin/queryFragment", headers=headers 71 | ).json() 72 | if fragment_res["success"]: 73 | return ( 74 | f'账号:{data["name"]} 签到成功\n' f'碎片余额:{fragment_res["data"]["total"]}' 75 | ) 76 | except json.decoder.JSONDecodeError: 77 | return "查询请求失败" 78 | 79 | return "未知错误" 80 | 81 | def main(self): 82 | msg_all = "" 83 | for check_item in self.check_items: 84 | msg = self.checkin( 85 | openid=str(check_item.get("openid")), uid=str(check_item.get("uid")) 86 | ) 87 | msg_all += msg + "\n\n" 88 | return msg_all 89 | 90 | 91 | if __name__ == "__main__": 92 | _data = get_data() 93 | _check_items = _data.get("HAIDILAO", []) 94 | result = Haidilao(check_items=_check_items).main() 95 | send("海底捞会员签到", result) 96 | -------------------------------------------------------------------------------- /ck_hifini.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 0 0 0 * * * 4 | new Env('HiFiNi'); 5 | """ 6 | 7 | import requests 8 | 9 | from notify_mtr import send 10 | from utils import get_data 11 | 12 | 13 | class HiFiNi: 14 | def __init__(self, check_items): 15 | self.check_items = check_items 16 | 17 | @staticmethod 18 | def signin(cookies): 19 | sign_in_url = "https://www.hifini.com/sg_sign.htm" 20 | data = {"x-requested-with": "XMLHttpRequest"} 21 | cookies = {"enwiki_session": f"{cookies}"} 22 | r1 = requests.post(sign_in_url, data=data, cookies=cookies) 23 | html_text = r1.text 24 | is_sign = False 25 | msg = "" 26 | for line in html_text.splitlines(): 27 | if line.find("今天已经签过啦") != -1: 28 | msg = "今天已经签过啦" 29 | is_sign = True 30 | if not is_sign: 31 | msg = "签到成功!" 32 | return msg 33 | 34 | def main(self): 35 | msg_all = "" 36 | for i, check_item in enumerate(self.check_items, start=1): 37 | cookie = check_item.get("cookie") 38 | msg = f"账号{i}\n{self.signin(cookie)}" 39 | msg_all += msg + "\n\n" 40 | return msg_all 41 | 42 | 43 | if __name__ == "__main__": 44 | _data = get_data() 45 | _check_items = _data.get("HIFINI", []) 46 | result = HiFiNi(check_items=_check_items).main() 47 | send("HiFiNi", result) 48 | -------------------------------------------------------------------------------- /ck_hlx.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 30 7 * * * 4 | new Env('葫芦侠'); 5 | """ 6 | 7 | import hashlib 8 | 9 | import requests 10 | from bs4 import BeautifulSoup 11 | 12 | from notify_mtr import send 13 | from utils import get_data 14 | 15 | 16 | class HLX: 17 | def __init__(self, check_items): 18 | self.check_items = check_items 19 | 20 | @staticmethod 21 | def md5(password): 22 | m = hashlib.md5() 23 | b = password.encode(encoding="utf-8") 24 | m.update(b) 25 | return m.hexdigest() 26 | 27 | def login(self, username, password): 28 | password_md5 = self.md5(password) 29 | url = "https://floor.huluxia.com/account/login/IOS/4.0" 30 | headers = {"Content-Type": "application/x-www-form-urlencoded"} 31 | data = { 32 | "account": username, 33 | "deviceCode": "", 34 | "device_code": "", 35 | "login_type": "2", 36 | "password": password_md5, 37 | } 38 | res = requests.post(url, data=data, headers=headers).json() 39 | key = res["_key"] 40 | nick = res["user"]["nick"] 41 | userID = res["user"]["userID"] 42 | msg = f"[+]用户: {nick} ({userID})" 43 | return key, userID, msg 44 | 45 | @staticmethod 46 | def get_level(userID, key): 47 | url = f"http://floor.huluxia.com/view/level?viewUserID={userID}&_key={key}" 48 | response = requests.post(url) 49 | soup = BeautifulSoup(response.text, "html.parser") # 解析 html 页面 50 | level = soup.select(".lev_li_forth span") # 筛选经验值 51 | return ( 52 | f"[+]当前经验: {level[0].string}\n" 53 | f"[+]距下一级: {level[1].string} 还需:{level[2].string} 经验" 54 | ) 55 | 56 | @staticmethod 57 | def sign(key): 58 | # 获取所有板块 url 59 | url = "https://floor.huluxia.com/category/forum/list/IOS/1.0" 60 | # 获取所有板块下的内容 url 61 | url_all = "https://floor.huluxia.com/category/forum/list/all/IOS/1.0" 62 | # 签到板块 url 63 | sign_url = "https://floor.huluxia.com/user/signin/IOS/1.1" 64 | # 获取所有板块 65 | result = "" 66 | for i in requests.post(url).json()["categoryforum"]: 67 | # 获取所有板块下的内容 68 | res = requests.post(url_all, data={"fum_id": i["id"]}).json() 69 | for cat in res["categories"]: 70 | headers = { 71 | "Host": "floor.huluxia.com", 72 | "Content-Type": "application/x-www-form-urlencoded", 73 | "Connection": "keep-alive", 74 | "Accept": "*/*", 75 | "User-Agent": "Floor/1.3.0 (iPhone; iOS 15.3; Scale/3.00)", 76 | "Accept-Language": "zh-Hans-CN;q=1", 77 | "Content-Length": "304", 78 | "Accept-Encoding": "gzip, deflate, br", 79 | } 80 | res2 = requests.post( 81 | sign_url, 82 | data={"_key": key, "cat_id": cat["categoryID"]}, 83 | headers=headers, 84 | ).json() 85 | if res2["status"] == 0: 86 | result += f'\n[+]{cat["title"]} 签到失败 错误原因:{res2["msg"]}' 87 | elif res2["status"] == 1: 88 | result += f'\n[+]{cat["title"]} 签到成功 获得经验:{res2["experienceVal"]}' 89 | return result 90 | 91 | def main(self): 92 | msg_all = "" 93 | for check_item in self.check_items: 94 | username = check_item.get("username") 95 | password = check_item.get("password") 96 | key, userID, login_msg = self.login(username, password) 97 | level_msg = self.get_level(userID, key) 98 | sign_msg = self.sign(key) 99 | msg = login_msg + "\n" + level_msg + sign_msg 100 | msg_all += msg + "\n\n" 101 | return msg_all 102 | 103 | 104 | def start(): 105 | _data = get_data() 106 | _check_items = _data.get("HLX", []) 107 | result = HLX(check_items=_check_items).main() 108 | send("葫芦侠", result) 109 | 110 | 111 | if __name__ == "__main__": 112 | start() 113 | -------------------------------------------------------------------------------- /ck_hostloc.py: -------------------------------------------------------------------------------- 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"(.*?)", 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 | -------------------------------------------------------------------------------- /ck_juejin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 7 11 * * * 4 | new Env('掘金'); 5 | """ 6 | 7 | import requests 8 | 9 | from notify_mtr import send 10 | from utils import get_data 11 | 12 | 13 | class Juejin: 14 | def __init__(self, check_items): 15 | self.check_items = check_items 16 | self.base_url = "https://api.juejin.cn/" 17 | self.headers = { 18 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 19 | "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36" 20 | } 21 | 22 | def sign(self, cookie): 23 | sign_url = f"{self.base_url}growth_api/v1/check_in" 24 | return requests.post( 25 | url=sign_url, headers=self.headers, cookies={"Cookie": cookie} 26 | ).json() 27 | 28 | def lottery(self, cookie): 29 | lottery_url = f"{self.base_url}growth_api/v1/lottery/draw" 30 | return requests.post( 31 | url=lottery_url, headers=self.headers, cookies={"Cookie": cookie} 32 | ).json() 33 | 34 | def main(self): 35 | msg_all = "" 36 | for i, check_item in enumerate(self.check_items, start=1): 37 | cookie = str(check_item.get("cookie")) 38 | sign_msg = self.sign(cookie)["err_msg"] 39 | lottery_msg = self.lottery(cookie)["err_msg"] 40 | msg = ( 41 | f"账号 {i}" 42 | + "\n------ 掘金签到结果 ------\n" 43 | + sign_msg 44 | + "\n------ 掘金抽奖结果 ------\n" 45 | + lottery_msg 46 | ) 47 | msg_all += msg + "\n\n" 48 | return msg_all 49 | 50 | 51 | if __name__ == "__main__": 52 | _data = get_data() 53 | _check_items = _data.get("JUEJIN", []) 54 | result = Juejin(check_items=_check_items).main() 55 | send("掘金", result) 56 | -------------------------------------------------------------------------------- /ck_lenovo.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @url: https://raw.githubusercontent.com/Wenmoux/checkbox/master/scripts/lenovo.js 3 | * @author: wenmoux 4 | 10 22 * * * ck_lenovo.js 5 | */ 6 | 7 | const axios = require('axios'); 8 | const CryptoJS = require('crypto-js'); 9 | 10 | const utils = require('./utils'); 11 | const Env = utils.Env; 12 | const getData = utils.getData; 13 | 14 | const $ = new Env('联想商城'); 15 | const notify = $.isNode() ? require('./notify') : ''; 16 | const ACCOUNTS_LENOVO = getData().LENOVO; 17 | 18 | const url = { 19 | login: '', 20 | session: '', 21 | sign1: 'https://api.club.lenovo.cn/common/signin/add', 22 | sign2: 'https://api.club.lenovo.cn/signin/v2/add', 23 | }; 24 | const headers = { 25 | baseinfo: '', 26 | unique: '', 27 | 'User-Agent': 'LenovoClub/5.1.3 (iPad; iOS 14.7.1; Scale/2.00)', 28 | token: '', 29 | //"User-Agent":"Apache-HttpClient/UNAVAILABLE (java 1.5)", 30 | Authorization: '', 31 | itemid: '1', 32 | sversion: '0', 33 | 'X-Lenovo-APPID': '1', 34 | versionCode: '1000082', 35 | }; 36 | 37 | var desp = ''; 38 | 39 | lenovo(); 40 | 41 | async function lenovo() { 42 | if (ACCOUNTS_LENOVO) { 43 | Log('account 数量:' + ACCOUNTS_LENOVO.length); 44 | for (let a = 0; a < ACCOUNTS_LENOVO.length; a++) { 45 | let account = ACCOUNTS_LENOVO[a].account; 46 | let password = ACCOUNTS_LENOVO[a].password; 47 | let baseinfo = ACCOUNTS_LENOVO[a].baseinfo 48 | ? ACCOUNTS_LENOVO[a].baseinfo 49 | : 'eyJpbWVpIjoiODY1MzE1MDMxOTg1ODc4IiwicGhvbmVicmFuZCI6Imhvbm9yIiwicGhvbmVNb2RlbCI6IkZSRC1BTDEwIiwiYXBwVmVyc2lvbiI6IlY0LjIuNSIsInBob25laW5jcmVtZW50YWwiOiI1NTYoQzAwKSIsIlBhZ2VJbmZvIjoiTXlJbmZvcm1hdGlvbkFjdGlvbkltcGwiLCJwaG9uZWRpc3BsYXkiOiJGUkQtQUwxMCA4LjAuMC41NTYoQzAwKSIsInBob25lTWFudWZhY3R1cmVyIjoiSFVBV0VJIiwibGVub3ZvQ2x1YkNoYW5uZWwiOiJ5aW5neW9uZ2JhbyIsImxvZ2luTmFtZSI6IjE3NjQwNDA4NTM3IiwicGhvbmVwcm9kdWN0IjoiRlJELUFMMTAiLCJzeXN0ZW1WZXJzaW9uIjoiOC4wLjAiLCJhbmRyb2lkc2RrdmVyc2lvbiI6IjI2In0='; 50 | let parsedWordArray = CryptoJS.enc.Base64.parse(baseinfo); 51 | let info = JSON.parse(parsedWordArray.toString(CryptoJS.enc.Utf8)); 52 | let deviceid = info.imei; 53 | url.login = `https://uss.lenovomm.com/authen/1.2/tgt/user/get?msisdn=${account}`; 54 | headers.baseinfo = baseinfo; 55 | headers.unique = deviceid; 56 | Log('\n======== [Account ' + (a + 1) + '] Start ======== '); 57 | let lpsutgt = await lxlogin(deviceid, info, password); 58 | let session = await getsession(lpsutgt); 59 | if (session) { 60 | await addsign(session, deviceid); 61 | } 62 | } 63 | } else { 64 | Log(`【联想延保每日签到】:请填写联想商城抓包数据`); 65 | } 66 | notify.sendNotify('联想商城', desp); 67 | return '联想商城' + '\n\n' + desp; 68 | } 69 | 70 | function lxlogin(deviceid, info, password) { 71 | return new Promise(async (resolve) => { 72 | try { 73 | let data = `lang=zh-CN-%23Hans&source=android%3Acom.lenovo.club.app-V4.2.5&deviceidtype=mac&deviceid=${deviceid}&devicecategory=unknown&devicevendor=${info.phoneManufacturer}&devicefamily=unknown&devicemodel=${info.phoneModel}&osversion=${info.systemVersion}&osname=Android&password=${password}`; 74 | let res = await axios.post(url['login'], data); 75 | let lpsutgt = res.data.match(/(.+?)<\/Value>/); 76 | if (lpsutgt) { 77 | let res2 = await axios.get( 78 | `https://uss.lenovomm.com/authen/1.2/st/get?lpsutgt=${lpsutgt[1]}&source=ios%3Alenovo%3Aclub%3A4.1.0&lang=zh-CN&realm=club.lenovo.com.cn` 79 | ); 80 | let lpsutgt2 = res2.data.match(/(.+?)<\/Value>/); 81 | lpsutgt = lpsutgt2 ? lpsutgt2[1] : null; 82 | } 83 | Log('登录成功!'); 84 | resolve(lpsutgt); 85 | // 预约游戏id 86 | } catch (err) { 87 | lpsutgt = null; 88 | Log(`登录失败!${err.response}`); 89 | } 90 | resolve(); 91 | }); 92 | } 93 | 94 | function getsession(lpsutgt) { 95 | return new Promise(async (resolve) => { 96 | try { 97 | headers.Authorization = 'Lenovosso ' + lpsutgt; 98 | headers['token'] = headers.Authorization + '=='; 99 | let s = aesEncrypto(`{"sessionid":"Lenovosso ${lpsutgt}","time":"${new Date().getTime()}"}`); 100 | url['session'] = 'https://api.club.lenovo.cn/users/getSessionID' + `?s=${s}`; 101 | let res3 = await axios.get(url['session'], { 102 | headers, 103 | }); 104 | let json = { 105 | lenovoid: res3.data.res.lenovoid, 106 | sessionid: res3.data.res.sessionid, 107 | token: res3.data.res.token, 108 | }; 109 | resolve(json); 110 | } catch (err) { 111 | Log(`获取token失败 ${decodeURI(err.response.data.res.error_CN)}`); 112 | } 113 | resolve(); 114 | }); 115 | } 116 | 117 | function addsign(session, deviceid) { 118 | return new Promise(async (resolve) => { 119 | try { 120 | headers.Authorization = 'Lenovo ' + session.sessionid; 121 | headers['token'] = session.token + '=='; 122 | headers['User-Agent'] = 'Apache-HttpClient/UNAVAILABLE (java 1.5)'; 123 | headers['Content-Type'] = 'text/json'; 124 | let data = aesEncrypto( 125 | `{"uid":"${session.lenovoid}","imei":"${deviceid}","source":"0","sessionid":"Lenovo ${session.sessionid}","time":"${new Date().getTime()}"}` 126 | ); 127 | let res = await axios.post(url['sign2'], data, { 128 | headers, 129 | }); 130 | if (typeof res.data === 'object' && res.data.status == 0) { 131 | // msg+=res.data.res.add_yb_tip 132 | if (!res.data.res.success) { 133 | Log('今天已经签到过啦'); 134 | } else { 135 | Log('签到成功\n' + res.data.res.rewardTips + '\n连续签到' + res.data.res.continueCount + '天'); 136 | } 137 | } 138 | } catch (err) { 139 | Log(`签到失败 ${decodeURI(err.response.data.res.error_CN)}`); 140 | } 141 | resolve(); 142 | }); 143 | } 144 | 145 | function aesEncrypto(text) { 146 | let key = CryptoJS.enc.Utf8.parse('nihao_liu#zh*9@7'); 147 | let iv = CryptoJS.enc.Utf8.parse('A*8@Stii_jin)*%6'); 148 | let encrypto = CryptoJS.AES.encrypt(text, key, { 149 | iv: iv, 150 | mode: CryptoJS.mode.CBC, 151 | padding: CryptoJS.pad.Pkcs7, 152 | }); 153 | return encrypto.ciphertext.toString(); 154 | } 155 | 156 | function Log(info) { 157 | console.log(info); 158 | desp = desp + info + '\n'; 159 | return desp; 160 | } 161 | 162 | module.exports = lenovo; 163 | -------------------------------------------------------------------------------- /ck_meizu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 55 15 * * * 4 | new Env('MEIZU 社区'); 5 | """ 6 | 7 | import time 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class Meizu: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | self.url = "https://bbs-act.meizu.cn/index.php" 19 | 20 | def sign(self, cookie): 21 | headers = { 22 | "authority": "bbs-act.meizu.cn", 23 | "pragma": "no-cache", 24 | "cache-control": "no-cache", 25 | "accept": "application/json, text/javascript, */*; q=0.01", 26 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) " 27 | "AppleWebKit/537.36 (KHTML, like Gecko) " 28 | "Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74", 29 | "origin": "https://bbs.meizu.cn", 30 | "referer": "https://bbs.meizu.cn/", 31 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", 32 | "cookie": cookie, 33 | } 34 | params = (("mod", "signin"), ("action", "sign")) 35 | res = requests.get(self.url, headers=headers, params=params).json() 36 | return res.get("message") 37 | 38 | def draw(self, cookie, count=0): 39 | headers = { 40 | "authority": "bbs-act.meizu.cn", 41 | "accept": "application/json, text/javascript, */*; q=0.01", 42 | "x-requested-with": "XMLHttpRequest", 43 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) " 44 | "AppleWebKit/537.36 (KHTML, like Gecko) " 45 | "Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74", 46 | "content-type": "application/x-www-form-urlencoded", 47 | "origin": "https://bbs-act.meizu.cn", 48 | "referer": "https://bbs-act.meizu.cn/2/index.html", 49 | "accept-language": "zh-CN,zh;q=0.9", 50 | "cookie": cookie, 51 | } 52 | data = {"mod": "index", "action": "draw", "id": "2"} 53 | award_list = [] 54 | if count: 55 | success_count = 0 56 | for i in range(count): 57 | try: 58 | res = requests.post(self.url, data, headers=headers).json() 59 | if res["code"] == 200: 60 | one_msg = res.get("data", {}).get("award_name") 61 | award_list.append(one_msg) 62 | success_count += 1 63 | else: 64 | print(res.get("code"), res.get("message")) 65 | one_msg = "抽奖失败" 66 | except Exception as e: 67 | one_msg = f"抽奖失败: {e}" 68 | print(f"第 {i + 1} 次抽奖结果: {str(one_msg)}") 69 | time.sleep(5) 70 | msg = f"成功抽奖 {success_count} 次" 71 | draw_msg = f"抽奖状态: {str(msg)}" 72 | draw_msg += f"\n抽奖结果: {';'.join(award_list)}" 73 | else: 74 | draw_msg = "抽奖结果: 未开启抽奖" 75 | data = {"mod": "index", "action": "get_user_count", "id": "2"} 76 | user_info = requests.post(self.url, data, headers=headers).json() 77 | uid = user_info.get("data", {}).get("uid") 78 | return draw_msg, uid 79 | 80 | def main(self): 81 | msg_all = "" 82 | for check_item in self.check_items: 83 | cookie = check_item.get("cookie") 84 | try: 85 | draw_count = int(check_item.get("draw_count", 0)) 86 | except Exception as e: 87 | print("初始化抽奖次数失败: 重置为 0 ", e) 88 | draw_count = 0 89 | sign_msg = self.sign(cookie) 90 | draw_msg, uid = self.draw(cookie, draw_count) 91 | msg = f"帐号信息: {uid}\n签到信息: {sign_msg}\n{draw_msg}" 92 | msg_all += msg + "\n\n" 93 | return msg_all 94 | 95 | 96 | if __name__ == "__main__": 97 | _data = get_data() 98 | _check_items = _data.get("MEIZU", []) 99 | result = Meizu(check_items=_check_items).main() 100 | send("MEIZU 社区", result) 101 | -------------------------------------------------------------------------------- /ck_oneplusbbs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 3 0 * * * 4 | new Env('一加手机社区官方论坛'); 5 | """ 6 | 7 | import re 8 | import time 9 | from urllib import parse 10 | 11 | import requests 12 | 13 | from notify_mtr import send 14 | from utils import get_data 15 | 16 | 17 | class OnePlusBBS: 18 | def __init__(self, check_items): 19 | self.check_items = check_items 20 | 21 | @staticmethod 22 | def sign(cookie): 23 | headers = { 24 | "Origin": "https://www.oneplusbbs.com", 25 | "Content-Type": "application/x-www-form-urlencoded", 26 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 27 | "AppleWebKit/537.36 (KHTML, like Gecko) " 28 | "Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.57", 29 | "Accept": "text/html,application/xhtml+xml,application/xml;" 30 | "q=0.9,image/webp,image/apng,*/*;" 31 | "q=0.8,application/signed-exchange;v=b3;q=0.9", 32 | "Referer": "https://www.oneplusbbs.com/plugin-dsu_paulsign:sign.html", 33 | "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,fr;q=0.5,pl;q=0.4", 34 | "cookie": cookie, 35 | } 36 | params = ( 37 | ("id", "dsu_paulsign:sign"), 38 | ("operation", "qiandao"), 39 | ("infloat", "1"), 40 | ("inajax", "1"), 41 | ) 42 | formhash = re.findall(r"bbs_formhash=(.*?);", cookie)[0] 43 | data = {"formhash": formhash, "qdxq": "kx", "qdmode": "1", "todaysay": "努力奋斗"} 44 | res = requests.post( 45 | "https://www.oneplusbbs.com/plugin.php", 46 | data, 47 | params=params, 48 | headers=headers, 49 | ).text 50 | msg = re.findall(r'
(.*?)
', res, re.S) 51 | msg = msg[0].strip() if msg else "Cookie 可能过期" 52 | return msg 53 | 54 | @staticmethod 55 | def draw(cookie): 56 | headers = { 57 | "Accept": "application/json, text/javascript, */*; q=0.01", 58 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 59 | "AppleWebKit/537.36 (KHTML, like Gecko) " 60 | "Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.57", 61 | "X-Requested-With": "XMLHttpRequest", 62 | "Origin": "https://www.oneplusbbs.com", 63 | "Referer": "https://www.oneplusbbs.com/plugin-choujiang.html", 64 | "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,fr;q=0.5,pl;q=0.4", 65 | "cookie": cookie, 66 | } 67 | params = (("id", "choujiang"), ("do", "draw")) 68 | sum_list = [] 69 | success_count = 0 70 | for i in range(10): 71 | try: 72 | res = requests.post( 73 | "https://www.oneplusbbs.com/plugin.php", 74 | params=params, 75 | headers=headers, 76 | ).json() 77 | if res["ret"] != "": 78 | ret = res["ret"] 79 | ret_map = {"2": 18, "4": 188, "5": 88, "7": 8} 80 | sum_list.append(ret_map.get(ret, 0)) 81 | one_msg = res["msg"] 82 | if str(ret) in {"-1", "-6", "-7"}: 83 | break 84 | success_count += 1 85 | else: 86 | one_msg = "抽奖失败" 87 | except Exception as e: 88 | one_msg = f"抽奖失败: {e}" 89 | print(f"第 {i + 1} 次抽奖结果:{str(one_msg)}") 90 | time.sleep(5) 91 | msg = f"成功抽奖 {success_count} 次" 92 | draw_msg = f"抽奖状态: {str(msg)}" 93 | draw_msg += f"\n抽奖结果: 获得 {sum(sum_list) - success_count * 10} 加油" 94 | print(draw_msg) 95 | return draw_msg 96 | 97 | def main(self): 98 | msg_all = "" 99 | for check_item in self.check_items: 100 | cookie = check_item.get("cookie") 101 | bbs_uname = re.findall(r"bbs_uname=(.*?);", cookie) 102 | bbs_uname = bbs_uname[0].split("%7C")[0] if bbs_uname else "未获取到用户信息" 103 | try: 104 | bbs_uname = parse.unquote(bbs_uname) 105 | except Exception as e: 106 | print(f"bbs_uname 转换失败: {e}") 107 | sign_msg = self.sign(cookie) 108 | draw_msg = self.draw(cookie) 109 | msg = f"帐号信息: {bbs_uname}\n签到信息: {sign_msg}\n{draw_msg}" 110 | msg_all += msg + "\n\n" 111 | return msg_all 112 | 113 | 114 | if __name__ == "__main__": 115 | _data = get_data() 116 | _check_items = _data.get("ONEPLUSBBS", []) 117 | result = OnePlusBBS(check_items=_check_items).main() 118 | send("一加手机社区官方论坛", result) 119 | -------------------------------------------------------------------------------- /ck_picacomic.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 45 5 * * * 4 | new Env('哔咔漫画'); 5 | """ 6 | 7 | import hashlib 8 | import hmac 9 | import random 10 | import string 11 | import time 12 | 13 | import requests 14 | 15 | from notify_mtr import send 16 | from utils import get_data 17 | 18 | 19 | class Picacomic: 20 | def __init__(self, check_items): 21 | self.check_items = check_items 22 | 23 | @staticmethod 24 | def generate_headers(path, data=None, token=None): 25 | api_key = "C69BAF41DA5ABD1FFEDC6D2FEA56B" 26 | api_secret = "~d}$Q7$eIni=V)9\\RK/P.RM4;9[7|@/CA}b~OW!3?EV`:<>M7pddUBL5n|0/*Cn" 27 | current_time = str(int(time.time())) 28 | nonce = "".join(random.choices(string.ascii_lowercase + string.digits, k=32)) 29 | raw = path + current_time + nonce + "POST" + api_key 30 | raw = raw.lower() 31 | h = hmac.new(api_secret.encode(), digestmod=hashlib.sha256) 32 | h.update(raw.encode()) 33 | signature = h.hexdigest() 34 | headers = { 35 | "api-key": api_key, 36 | "accept": "application/vnd.picacomic.com.v1+json", 37 | "app-channel": "2", 38 | "app-version": "2.2.1.2.3.3", 39 | "app-uuid": "defaultUuid", 40 | "app-platform": "android", 41 | "app-build-version": "44", 42 | "User-Agent": "okhttp/3.8.1", 43 | "image-quality": "original", 44 | "time": current_time, 45 | "nonce": nonce, 46 | "signature": signature, 47 | } 48 | 49 | if data is not None: 50 | headers["Content-Type"] = "application/json; charset=UTF-8" 51 | if token is not None: 52 | headers["authorization"] = token 53 | return headers 54 | 55 | def sign(self, email, password): 56 | try: 57 | data = {"email": email, "password": password} 58 | sign_headers = self.generate_headers(path="auth/sign-in", data=data) 59 | sign_res = requests.post( 60 | "https://picaapi.picacomic.com/auth/sign-in", 61 | json={"email": email, "password": password}, 62 | headers=sign_headers, 63 | timeout=60, 64 | ).json() 65 | token = sign_res.get("data", {}).get("token") 66 | 67 | punch_headers = self.generate_headers(path="users/punch-in", token=token) 68 | res = requests.post( 69 | "https://picaapi.picacomic.com/users/punch-in", 70 | headers=punch_headers, 71 | timeout=60, 72 | ).json() 73 | if res.get("data", {}).get("res", {}).get("status", {}) == "ok": 74 | msg = "打卡成功" 75 | else: 76 | msg = "重复签到" 77 | except Exception as e: 78 | msg = str(e) 79 | return msg 80 | 81 | def main(self): 82 | msg_all = "" 83 | for check_item in self.check_items: 84 | email = check_item.get("email") 85 | password = check_item.get("password") 86 | sign_msg = self.sign(email, password) 87 | msg = f"帐号信息: {email}\n签到状态: {sign_msg}" 88 | msg_all += msg + "\n\n" 89 | return msg_all 90 | 91 | 92 | if __name__ == "__main__": 93 | _data = get_data() 94 | _check_items = _data.get("PICACOMIC", []) 95 | result = Picacomic(check_items=_check_items).main() 96 | send("哔咔漫画", result) 97 | -------------------------------------------------------------------------------- /ck_pojie.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 53 11 * * * 4 | new Env('吾爱破解'); 5 | """ 6 | 7 | import requests 8 | from bs4 import BeautifulSoup 9 | 10 | from notify_mtr import send 11 | from utils import get_data 12 | 13 | 14 | class Pojie: 15 | def __init__(self, check_items): 16 | self.check_items = check_items 17 | 18 | @staticmethod 19 | def sign(cookie): 20 | session = requests.session() 21 | res = "" 22 | headers = { 23 | "Cookie": cookie, 24 | "ContentType": "text/html;charset=gbk", 25 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) " 26 | "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", 27 | } 28 | session.put( 29 | "https://www.52pojie.cn/home.php?mod=task&do=apply&id=2", headers=headers 30 | ) 31 | response = session.put( 32 | "https://www.52pojie.cn/home.php?mod=task&do=draw&id=2", headers=headers 33 | ) 34 | soup = BeautifulSoup(response.text, "html.parser") 35 | msg = soup.find("div", id="messagetext").find("p").text 36 | if "您需要先登录才能继续本操作" in msg: 37 | res += "Cookie 失效" 38 | elif "恭喜" in msg: 39 | res += "签到成功" 40 | elif "不是进行中的任务" in msg: 41 | res += "不是进行中的任务" 42 | else: 43 | res += "签到失败" 44 | return res 45 | 46 | def main(self): 47 | msg_all = "" 48 | for i, check_item in enumerate(self.check_items, start=1): 49 | cookie = check_item.get("cookie") 50 | sign_msg = self.sign(cookie) 51 | msg = f"账号 {i} 签到状态: {sign_msg}" 52 | msg_all += msg + "\n\n" 53 | return msg_all 54 | 55 | 56 | if __name__ == "__main__": 57 | _data = get_data() 58 | _check_items = _data.get("POJIE", []) 59 | result = Pojie(check_items=_check_items).main() 60 | send("吾爱破解", result) 61 | -------------------------------------------------------------------------------- /ck_site.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | :author @gnodgl 4 | cron: 11 6 * * * 5 | new Env('Site'); 6 | """ 7 | 8 | import json 9 | import re 10 | from json.decoder import JSONDecodeError 11 | 12 | import requests 13 | 14 | from notify_mtr import send 15 | from utils import get_data 16 | 17 | desp = "" 18 | 19 | 20 | def log(info): 21 | global desp 22 | desp = desp + info + "\n\n" 23 | 24 | 25 | class Site: 26 | def __init__(self, check_items): 27 | self.check_items = check_items 28 | self.error_tip = "cookie 已过期或网站类型不对" 29 | 30 | @staticmethod 31 | def generate_headers(url): 32 | return { 33 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 34 | "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36", 35 | "Accept-Language": "zh-CN,zh;q=0.9", 36 | "Referer": url, 37 | } 38 | 39 | @staticmethod 40 | def cookie_parse(cookie_str): 41 | cookie_dict = {} 42 | cookies = cookie_str.split(";") 43 | for cookie in cookies: 44 | cookie = cookie.split("=") 45 | cookie_dict[cookie[0]] = cookie[1] 46 | return cookie_dict 47 | 48 | def signin(self, session, url): 49 | # hdarea 签到 50 | if url == "https://www.hdarea.co": 51 | self.hdarea(session, url) 52 | elif url == "https://pterclub.com": 53 | self.pterclub(session, url) 54 | elif url == "https://www.haidan.video": 55 | self.haidan(session, url) 56 | elif url == "https://pt.btschool.club": 57 | self.btschool(session, url) 58 | elif url == "https://lemonhd.org": 59 | self.lemonhd(session, url) 60 | elif url in ["https://hdtime.org", "https://www.pttime.org"]: 61 | self.hdtime(session, url) 62 | else: 63 | self.common(session, url) 64 | 65 | @staticmethod 66 | def signin_base(session, url, data=None, **kwargs): 67 | attendance_url = kwargs.get("attendance_url") 68 | success = kwargs.get("success", []) 69 | repeat = kwargs.get("repeat", []) 70 | failure = kwargs.get("failure", "") 71 | with session.get(attendance_url, data) as response: 72 | r1 = re.compile(success[0]) 73 | r2 = re.compile(repeat[0]) 74 | location = r1.search(response.text) 75 | if location: 76 | tip = success[1] 77 | elif r2.search(response.text): 78 | tip = repeat[1] 79 | else: 80 | tip = failure 81 | print(f"{url} {tip}") 82 | if tip == "btschool": 83 | tip = location.group() 84 | log(f"{url} {tip}") 85 | 86 | def hdarea(self, session, url): 87 | self.signin_base( 88 | session, 89 | url, 90 | attendance_url=f"{url}/sign_in.php", 91 | data={"action": "sign_in"}, 92 | success=[r"获得了\d+魔力值", "签到成功"], 93 | repeat=[r"重复", "重复签到"], 94 | failure="签到失败", 95 | ) 96 | 97 | @staticmethod 98 | def pterclub(session, url): 99 | attendance_url = f"{url}/attendance-ajax.php" 100 | with session.get(attendance_url) as response: 101 | try: 102 | msg = json.loads( 103 | response.text.encode("utf-8").decode("unicode-escape") 104 | ).get("message") 105 | except JSONDecodeError: 106 | msg = response.text 107 | if "连续签到" in msg: 108 | pattern = re.compile(r"") 109 | tip = f"签到成功, {re.sub(pattern, '', msg)}" 110 | elif "重复刷新" in msg: 111 | tip = "重复签到" 112 | else: 113 | tip = "签到失败" 114 | print(f"{url} {tip}") 115 | log(f"{url} {tip}") 116 | 117 | def haidan(self, session, url): 118 | self.signin_base( 119 | session, 120 | url, 121 | attendance_url=f"{url}/signin.php", 122 | success=[r"已经打卡", "签到成功"], 123 | repeat=[r"退出", "重复签到"], 124 | failure="cookie 已过期或网站类型不对!", 125 | ) 126 | 127 | def btschool(self, session, url): 128 | self.signin_base( 129 | session, 130 | url, 131 | attendance_url=f"{url}/index.php?action=addbonus", 132 | success=[r"今天签到您获得\d+点魔力值", "btschool"], 133 | repeat=[r"退出", "重复签到"], 134 | failure="cookie 已过期", 135 | ) 136 | 137 | def lemonhd(self, session, url): 138 | self.signin_base( 139 | session, 140 | url, 141 | attendance_url=f"{url}/attendance.php", 142 | success=[r"已签到", "签到成功"], 143 | repeat=[r"请勿重复刷新", "重复签到"], 144 | failure="签到失败", 145 | ) 146 | 147 | def hdtime(self, session, url): 148 | self.signin_base( 149 | session, 150 | url, 151 | attendance_url=f"{url}/attendance.php", 152 | success=[r"签到成功", "签到成功"], 153 | repeat=[r"请勿重复刷新", "重复签到"], 154 | failure="cookie 已过期", 155 | ) 156 | 157 | @staticmethod 158 | def common(session, url): 159 | attendance_url = f"{url}/attendance.php" 160 | with session.get(attendance_url) as response: 161 | r = re.compile(r"请勿重复刷新") 162 | r1 = re.compile(r"签到已得\s*\d+") 163 | location = r1.search(response.text).span() 164 | if r.search(response.text): 165 | tip = "重复签到" 166 | elif location: 167 | tip = response.text[location[0], location[1]] 168 | else: 169 | tip = "cookie 已过期" 170 | print(f"{url} {tip}") 171 | log(f"{url} {tip}") 172 | 173 | @staticmethod 174 | def signin_discuz_dsu(session, url): 175 | attendance_url = ( 176 | f"{url}/plugin.php?" 177 | f"id=dsu_paulsign:sign&operation=qiandao&infloat=1&sign_as=1&inajax=1" 178 | ) 179 | hash_url = f"{url}/plugin.php?id=dsu_paulsign:sign" 180 | with session.get(hash_url) as hashurl: 181 | h = re.compile(r'name="formhash" value="(.*?)"') 182 | formhash = h.search(hashurl.text)[1] 183 | data = { 184 | "qdmode": 3, 185 | "qdxq": "kx", 186 | "fastreply": 0, 187 | "formhash": formhash, 188 | "todaysay": "", 189 | } 190 | with session.post(attendance_url, data) as response: 191 | r = re.compile(r"签到成功") 192 | r1 = re.compile(r"已经签到") 193 | if r.search(response.text): 194 | log(f"{url} 签到成功") 195 | elif r1.search(response.text): 196 | log(f"{url} 重复签到") 197 | else: 198 | log(f"{url} {response.text}") 199 | print(f"{url} {response.text}") 200 | 201 | @staticmethod 202 | def signin_hifi(session, url): 203 | attendance_url = f"{url}/sg_sign.htm" 204 | with session.post(attendance_url) as response: 205 | r = re.compile(r"成功") 206 | r1 = re.compile(r"今天已经") 207 | if r.search(response.text): 208 | log(f"{url} 签到成功") 209 | elif r1.search(response.text): 210 | log(f"{url} 重复签到") 211 | else: 212 | log(f"{url} {response.text}") 213 | print(f"{url} {response.text}") 214 | 215 | def main(self): 216 | for check_item in self.check_items: 217 | s = requests.session() 218 | url = check_item.get("url") 219 | site_type = check_item.get("type") 220 | cookie = self.cookie_parse(check_item.get("cookie")) 221 | header = self.generate_headers(url) 222 | s.headers.update(header) 223 | s.cookies.update(cookie) 224 | if site_type == "pt": 225 | self.signin(s, url) 226 | elif site_type == "discuz": 227 | self.signin_discuz_dsu(s, url) 228 | elif site_type == "hifi": 229 | self.signin_hifi(s, url) 230 | else: 231 | log("请在配置文件中配置网站类型,如 type: 'pt'") 232 | print("请在配置文件中配置网站类型,如 type: 'pt'") 233 | return desp 234 | 235 | 236 | if __name__ == "__main__": 237 | _data = get_data() 238 | _check_items = _data.get("SITE", []) 239 | result = Site(check_items=_check_items).main() 240 | send("Site", result) 241 | -------------------------------------------------------------------------------- /ck_smzdm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 51 9 * * * 4 | new Env('什么值得买'); 5 | """ 6 | 7 | from urllib.parse import quote, unquote 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class Smzdm: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def sign(session): 21 | try: 22 | current = session.get( 23 | url="https://zhiyou.smzdm.com/user/info/jsonp_get_current" 24 | ).json() 25 | if current["checkin"]["has_checkin"]: 26 | msg = ( 27 | f"用户信息: {current.get('nickname', '')}\n" 28 | f"目前积分: {current.get('point', '')}\n" 29 | f"经验值: {current.get('exp', '')}\n" 30 | f"金币: {current.get('gold', '')}\n" 31 | f"碎银子: {current.get('silver', '')}\n" 32 | f"威望: {current.get('prestige', '')}\n" 33 | f"等级: {current.get('level', '')}\n" 34 | f"已经签到: {current.get('checkin', {}).get('daily_checkin_num', '')} 天" 35 | ) 36 | else: 37 | data = ( 38 | session.get( 39 | url="https://zhiyou.smzdm.com/user/checkin/jsonp_checkin" 40 | ) 41 | .json() 42 | .get("data", {}) 43 | ) 44 | msg = ( 45 | f"用户信息: {current.get('nickname', '')}\n" 46 | f"目前积分: {data.get('point', '')}\n" 47 | f"增加积分: {data.get('add_point', '')}\n" 48 | f"经验值: {data.get('exp', '')}\n" 49 | f"金币: {data.get('gold', '')}\n" 50 | f"威望: {data.get('prestige', '')}\n" 51 | f"等级: {data.get('rank', '')}\n" 52 | f"已经签到: {data.get('checkin_num', {})} 天" 53 | ) 54 | except Exception as e: 55 | msg = f"签到状态: 签到失败\n错误信息: {e},请重新获取 cookie" 56 | return msg 57 | 58 | def main(self): 59 | msg_all = "" 60 | 61 | for check_item in self.check_items: 62 | cookie = { 63 | item.split("=")[0]: quote(unquote(item.split("=")[1])) 64 | for item in check_item.get("cookie").split("; ") 65 | if item.split("=")[0] == "sess" 66 | } 67 | session = requests.session() 68 | session.cookies.update(cookie) 69 | session.headers.update( 70 | { 71 | "Accept": "*/*", 72 | "Accept-Encoding": "gzip, deflate, br", 73 | "Accept-Language": "zh-CN,zh;q=0.9", 74 | "Connection": "keep-alive", 75 | "Host": "zhiyou.smzdm.com", 76 | "Referer": "https://www.smzdm.com/", 77 | "Sec-Fetch-Dest": "script", 78 | "Sec-Fetch-Mode": "no-cors", 79 | "Sec-Fetch-Site": "same-site", 80 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 81 | "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36", 82 | } 83 | ) 84 | msg = self.sign(session) 85 | msg_all += msg + "\n\n" 86 | return msg_all 87 | 88 | 89 | if __name__ == "__main__": 90 | _data = get_data() 91 | _check_items = _data.get("SMZDM", []) 92 | result = Smzdm(check_items=_check_items).main() 93 | send("什么值得买", result) 94 | -------------------------------------------------------------------------------- /ck_smzdm_app.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 51 9 * * * 4 | new Env('什么值得买APP'); 5 | """ 6 | 7 | 8 | import requests 9 | import json 10 | import time 11 | import hashlib 12 | 13 | from notify_mtr import send 14 | from utils import get_data 15 | 16 | 17 | class Smzdm: 18 | def __init__(self, check_items): 19 | self.check_items = check_items 20 | 21 | @staticmethod 22 | def sign(cookie): 23 | try: 24 | ts = int(round(time.time() * 1000)) 25 | url = 'https://user-api.smzdm.com/robot/token' 26 | headers = { 27 | 'Host': 'user-api.smzdm.com', 28 | 'Content-Type': 'application/x-www-form-urlencoded', 29 | 'Cookie': f'{cookie}', 30 | 'User-Agent': 'smzdm_android_V10.4.1 rv:841 (22021211RC;Android12;zh)smzdmapp', 31 | } 32 | data = { 33 | "f": "android", 34 | "v": "10.4.1", 35 | "weixin": 1, 36 | "time": ts, 37 | "sign": hashlib.md5(bytes(f'f=android&time={ts}&v=10.4.1&weixin=1&key=apr1$AwP!wRRT$gJ/q.X24poeBInlUJC', encoding='utf-8')).hexdigest().upper() 38 | } 39 | html = requests.post(url=url, headers=headers, data=data) 40 | res = html.json() 41 | token = res['data']['token'] 42 | 43 | Timestamp = int(round(time.time() * 1000)) 44 | data = { 45 | "f": "android", 46 | "v": "10.4.1", 47 | "sk": "ierkM0OZZbsuBKLoAgQ6OJneLMXBQXmzX+LXkNTuKch8Ui2jGlahuFyWIzBiDq/L", 48 | "weixin": 1, 49 | "time": Timestamp, 50 | "token": token, 51 | "sign": hashlib.md5(bytes(f'f=android&sk=ierkM0OZZbsuBKLoAgQ6OJneLMXBQXmzX+LXkNTuKch8Ui2jGlahuFyWIzBiDq/L&time={Timestamp}&token={token}&v=10.4.1&weixin=1&key=apr1$AwP!wRRT$gJ/q.X24poeBInlUJC', encoding='utf-8')).hexdigest().upper() 52 | } 53 | url = 'https://user-api.smzdm.com/checkin' 54 | url2 = 'https://user-api.smzdm.com/checkin/all_reward' 55 | headers = { 56 | 'Host': 'user-api.smzdm.com', 57 | 'Content-Type': 'application/x-www-form-urlencoded', 58 | 'Cookie': f'{cookie}', 59 | 'User-Agent': 'smzdm_android_V10.4.1 rv:841 (22021211RC;Android12;zh)smzdmapp', 60 | } 61 | html = requests.post(url=url, headers=headers, data=data) 62 | html2 = requests.post(url=url2, headers=headers, data=data) 63 | res = json.loads(html.text) 64 | res2 = json.loads(html2.text) 65 | if res2['error_code'] == '0': 66 | msg = res2["title"] + res2["sub_title"] 67 | else: 68 | msg = res['error_msg'] 69 | except Exception as e: 70 | msg = f"签到状态: 签到失败\n错误信息: {e},请重新获取 cookie" 71 | return msg 72 | 73 | def main(self): 74 | msg_all = "" 75 | 76 | for check_item in self.check_items: 77 | msg = self.sign(check_item.get("cookie")) 78 | msg_all += msg + "\n\n" 79 | return msg_all 80 | 81 | 82 | if __name__ == "__main__": 83 | _data = get_data() 84 | _check_items = _data.get("SMZDM", []) 85 | result = Smzdm(check_items=_check_items).main() 86 | send("什么值得买APP", result) 87 | -------------------------------------------------------------------------------- /ck_sspanel.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # shellcheck disable=SC2188 4 | <<'COMMENT' 5 | cron: 28 11 * * * 6 | new Env('SSPanel 签到'); 7 | COMMENT 8 | 9 | source "$(dirname "$0")/utils_env.sh" 10 | source "$(dirname "$0")/notify.sh" 11 | source_config 12 | check_jq_installed_status 13 | 14 | # 版本、初始化变量 15 | VERSION="2.2.2" 16 | TITLE="🚀SSPanel Auto Checkin v${VERSION}" 17 | users_array="" 18 | log_text="" 19 | COOKIE_PATH="./.ss-autocheckin.cook" 20 | PUSH_TMP_PATH="./.ss-autocheckin.tmp" 21 | 22 | # 加载用户组配置 23 | mapfile -t -d ';' users_array < <(echo "${SS_USERS}" | tr -d ' \r\n') 24 | 25 | # 签到 26 | ssp_autochenkin() { 27 | echo -e "${TITLE}" 28 | if [ "${users_array[*]}" ]; then 29 | user_count=1 30 | for user in "${users_array[@]}"; do 31 | domain=$(echo "${user}" | awk -F'----' '{print $1}') 32 | username=$(echo "${user}" | awk -F'----' '{print $2}') 33 | passwd=$(echo "${user}" | awk -F'----' '{print $3}') 34 | 35 | # 邮箱、域名脱敏处理 36 | username_prefix="${username%%@*}" 37 | username_suffix="${username#*@}" 38 | username_root="${username_suffix#*.}" 39 | username_text="${username_prefix:0:2}🙈@${username_suffix:0:2}🙈.${username_root}" 40 | 41 | domain_protocol="${domain%%://*}" 42 | domain_context="${domain##*//}" 43 | domain_root="${domain##*.}" 44 | domain_text="${domain_protocol}://${domain_context:0:2}🙈.${domain_root}" 45 | 46 | if [ -z "${domain}" ] || [ -z "${username}" ] || [ -z "${passwd}" ]; then 47 | echo "账号信息配置异常,请检查配置" && exit 1 48 | fi 49 | 50 | login=$(curl "${domain}/auth/login" -d "email=${username}&passwd=${passwd}&code=" -c ${COOKIE_PATH} -L -k -s) 51 | 52 | start_time=$(date '+%Y-%m-%d %H:%M:%S') 53 | login_code=$(echo "${login}" | jq -r '.ret' 2>&1) 54 | # login_status=$(echo "${login}" | jq -r '.msg' 2>&1) 55 | 56 | login_log_text="\n用户 ${user_count}\n" 57 | login_log_text="${login_log_text}签到站点: ${domain_text}\n" 58 | login_log_text="${login_log_text}签到用户: ${username_text}\n" 59 | login_log_text="${login_log_text}签到时间: ${start_time}\n" 60 | 61 | if [ "${login_code}" == "1" ]; then 62 | userinfo=$(curl -k -s -G -b ${COOKIE_PATH} "${domain}/getuserinfo") 63 | user=$(echo "${userinfo}" | tr '\r\n' ' ' | jq -r ".info.user" 2>&1) 64 | 65 | if [ "${user}" ]; then 66 | # 用户等级 67 | clasx=$(echo "${user}" | jq -r ".class" 2>&1) 68 | # 等级过期时间 69 | class_expire=$(echo "${user}" | jq -r ".class_expire" 2>&1) 70 | # 账户过期时间 71 | expire_in=$(echo "${user}" | jq -r ".expire_in" 2>&1) 72 | # 上次签到时间 73 | last_check_in_time=$(echo "${user}" | jq -r ".last_check_in_time" 2>&1) 74 | # 用户余额 75 | money=$(echo "${user}" | jq -r ".money" 2>&1) 76 | # 用户限速 77 | node_speedlimit=$(echo "${user}" | jq -r ".node_speedlimit" 2>&1) 78 | # 总流量 79 | transfer_enable=$(echo "${user}" | jq -r ".transfer_enable" 2>&1) 80 | # 总共使用流量 81 | last_day_t=$(echo "${user}" | jq -r ".last_day_t" 2>&1) 82 | # 剩余流量 83 | transfer_used=$(("${transfer_enable}" - "${last_day_t}")) 84 | # 转换 GB 85 | transfer_enable_text=$(echo "${transfer_enable}" | awk '{ byte =$1 /1024/1024**2 ; print byte " GB" }') 86 | last_day_t_text=$(echo "${last_day_t}" | awk '{ byte =$1 /1024/1024**2 ; print byte " GB" }') 87 | transfer_used_text=$(echo "${transfer_used}" | awk '{ byte =$1 /1024/1024**2 ; print byte " GB" }') 88 | # 转换上次签到时间 89 | if [ "${IS_MACOS}" -eq 0 ]; then 90 | last_check_in_time_text=$(date -d "1970-01-01 UTC ${last_check_in_time} seconds" "+%F %T") 91 | else 92 | last_check_in_time_text=$(date -r "${last_check_in_time}" '+%Y-%m-%d %H:%M:%S') 93 | fi 94 | 95 | user_log_text="\n用户等级: VIP${clasx}\n" 96 | user_log_text="${user_log_text}用户余额: ${money} CNY\n" 97 | user_log_text="${user_log_text}用户限速: ${node_speedlimit} Mbps\n" 98 | user_log_text="${user_log_text}总流量: ${transfer_enable_text}\n" 99 | user_log_text="${user_log_text}剩余流量: ${transfer_used_text}\n" 100 | user_log_text="${user_log_text}已使用流量: ${last_day_t_text}\n" 101 | user_log_text="${user_log_text}等级过期时间: ${class_expire}\n" 102 | user_log_text="${user_log_text}账户过期时间: ${expire_in}\n" 103 | user_log_text="${user_log_text}上次签到时间: ${last_check_in_time_text}" 104 | else 105 | user_log_text="" 106 | fi 107 | 108 | checkin=$(curl -k -s -d "" -b ${COOKIE_PATH} "${domain}/user/checkin") 109 | # chechin_code=$(echo "${checkin}" | jq -r ".ret" 2>&1) 110 | checkin_status=$(echo "${checkin}" | jq -r ".msg" 2>&1) 111 | 112 | if [ "${checkin_status}" ]; then 113 | checkin_log_text="签到状态: ${checkin_status}" 114 | else 115 | checkin_log_text="签到状态: 签到失败, 请检查是否存在签到验证码" 116 | fi 117 | 118 | result_log_text="${login_log_text}${checkin_log_text}${user_log_text}" 119 | else 120 | 121 | result_log_text="${login_log_text}签到状态: 登录失败, 请检查配置" 122 | fi 123 | 124 | if [ "${IS_DISPLAY_CONTEXT}" == 1 ]; then 125 | echo -e "${result_log_text}" 126 | else 127 | echo -e "\nHidden the logs, please view notify messages." 128 | fi 129 | 130 | log_text="${log_text}\n${result_log_text}" 131 | 132 | user_count=$((user_count + 1)) 133 | done 134 | 135 | log_text="${log_text}\n\n免费使用自: isecret(已被删库)\n适配青龙自: Oreo" 136 | 137 | send_message 138 | 139 | rm -rf ${COOKIE_PATH} 140 | rm -rf ${PUSH_TMP_PATH} 141 | else 142 | echo "用户组环境变量未配置" && exit 1 143 | fi 144 | } 145 | 146 | ssp_autochenkin 147 | -------------------------------------------------------------------------------- /ck_tieba.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 12 16 * * * 4 | new Env('百度贴吧'); 5 | """ 6 | 7 | import hashlib 8 | import re 9 | 10 | import requests 11 | 12 | from notify_mtr import send 13 | from utils import get_data 14 | 15 | 16 | class Tieba: 17 | def __init__(self, check_items): 18 | self.check_items = check_items 19 | 20 | @staticmethod 21 | def login_info(session): 22 | return session.get("https://zhidao.baidu.com/api/loginInfo").json() 23 | 24 | def valid(self, session): 25 | try: 26 | res = session.get("http://tieba.baidu.com/dc/common/tbs").json() 27 | except Exception as e: 28 | return False, f"登录验证异常,错误信息: {e}" 29 | if res["is_login"] == 0: 30 | return False, "登录失败,cookie 异常" 31 | tbs = res["tbs"] 32 | user_name = self.login_info(session=session)["userName"] 33 | return tbs, user_name 34 | 35 | @staticmethod 36 | def tieba_list_more(session): 37 | res = session.get( 38 | "https://tieba.baidu.com/f/like/mylike?&pn=1", 39 | timeout=(5, 20), 40 | allow_redirects=False, 41 | ).text 42 | try: 43 | pn = int( 44 | re.match(r".*/f/like/mylike\?&pn=(.*?)\">尾页.*", res, re.S | re.I)[1] 45 | ) 46 | 47 | except Exception: 48 | pn = 1 49 | pattern = re.compile(r".*?") 50 | for next_page in range(2, pn + 2): 51 | yield from pattern.findall(res) 52 | res = session.get( 53 | f"https://tieba.baidu.com/f/like/mylike?&pn={next_page}", 54 | timeout=(5, 20), 55 | allow_redirects=False, 56 | ).text 57 | 58 | def get_tieba_list(self, session): 59 | return list(self.tieba_list_more(session)) 60 | 61 | @staticmethod 62 | def sign(session, tb_name_list, tbs): 63 | success_count, error_count, exist_count, shield_count = 0, 0, 0, 0 64 | for tb_name in tb_name_list: 65 | md5 = hashlib.md5( 66 | f"kw={tb_name}tbs={tbs}tiebaclient!!!".encode("utf-8") 67 | ).hexdigest() 68 | data = {"kw": tb_name, "tbs": tbs, "sign": md5} 69 | try: 70 | res = session.post( 71 | url="http://c.tieba.baidu.com/c/c/forum/sign", data=data 72 | ).json() 73 | if res["error_code"] == "0": 74 | success_count += 1 75 | elif res["error_code"] == "160002": 76 | exist_count += 1 77 | elif res["error_code"] == "340006": 78 | shield_count += 1 79 | else: 80 | error_count += 1 81 | except Exception as e: 82 | print(f"贴吧 {tb_name} 签到异常,原因{str(e)}") 83 | return ( 84 | f"贴吧总数: {len(tb_name_list)}\n" 85 | f"签到成功: {success_count}\n" 86 | f"已经签到: {exist_count}\n" 87 | f"被屏蔽的: {shield_count}\n" 88 | f"签到失败: {error_count}" 89 | ) 90 | 91 | def main(self): 92 | msg_all = "" 93 | for check_item in self.check_items: 94 | cookie = { 95 | item.split("=")[0]: item.split("=")[1] 96 | for item in check_item.get("cookie").split("; ") 97 | } 98 | session = requests.session() 99 | session.cookies.update(cookie) 100 | session.headers.update({"Referer": "https://www.baidu.com/"}) 101 | tbs, user_name = self.valid(session) 102 | if tbs: 103 | tb_name_list = self.get_tieba_list(session) 104 | msg = f"帐号信息: {user_name}\n{self.sign(session, tb_name_list, tbs)}" 105 | else: 106 | msg = f"帐号信息: {user_name}\n签到状态: Cookie 可能过期" 107 | msg_all += msg + "\n\n" 108 | return msg_all 109 | 110 | 111 | if __name__ == "__main__": 112 | _data = get_data() 113 | _check_items = _data.get("TIEBA", []) 114 | result = Tieba(check_items=_check_items).main() 115 | send("百度贴吧", result) 116 | -------------------------------------------------------------------------------- /ck_toolu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 0 6 * * * 4 | new Env('在线工具'); 5 | """ 6 | 7 | import re 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class ToolLu: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def sign(cookie): 21 | url = "https://id.tool.lu/sign" 22 | headers = { 23 | "cookie": cookie, 24 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) " 25 | "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", 26 | } 27 | response = requests.get(url, headers=headers) 28 | day = re.findall("你已经连续签到(.*),再接再厉!", response.text) 29 | if len(day) == 0: 30 | return "cookie 失效" 31 | day = day[0].replace(" ", "") 32 | return f"连续签到 {day}" 33 | 34 | def main(self): 35 | msg_all = "" 36 | for check_item in self.check_items: 37 | cookie = check_item.get("cookie") 38 | msg = self.sign(cookie) 39 | msg_all += msg + "\n\n" 40 | return msg_all 41 | 42 | 43 | if __name__ == "__main__": 44 | _data = get_data() 45 | _check_items = _data.get("TOOLU", []) 46 | result = ToolLu(check_items=_check_items).main() 47 | send("在线工具", result) 48 | -------------------------------------------------------------------------------- /ck_v2ex.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 10 19 * * * 4 | new Env('V2EX'); 5 | """ 6 | 7 | import re 8 | 9 | import requests 10 | import urllib3 11 | 12 | from notify_mtr import send 13 | from utils import get_data 14 | 15 | urllib3.disable_warnings() 16 | 17 | 18 | class V2ex: 19 | def __init__(self, check_items): 20 | self.check_items = check_items 21 | self.url = "https://www.v2ex.com/mission/daily" 22 | 23 | def sign(self, session): 24 | msg = "" 25 | 26 | response = session.get(self.url, verify=False) 27 | pattern = ( 28 | r"" 30 | ) 31 | urls = re.findall(pattern, response.text) 32 | url = urls[0] if urls else None 33 | if url is None: 34 | return "cookie 可能过期" 35 | if url != "/balance": 36 | data = {"once": url.split("=")[-1]} 37 | session.get( 38 | f'https://www.v2ex.com{url.split("?")[0]}', verify=False, params=data 39 | ) 40 | 41 | response = session.get("https://www.v2ex.com/balance", verify=False) 42 | totals = re.findall( 43 | r"(\d+\.\d+)", 44 | response.text, 45 | ) 46 | total = totals[0] if totals else "签到失败" 47 | today = re.findall( 48 | r'(.*?)', response.text 49 | ) 50 | today = today[0] if today else "签到失败" 51 | 52 | usernames = re.findall( 53 | r"(.*?)", response.text 54 | ) 55 | username = usernames[0] if usernames else "用户名获取失败" 56 | 57 | msg += f"帐号信息: {username}\n今日签到: {today}\n帐号余额: {total}" 58 | 59 | response = session.get(url=self.url, verify=False) 60 | datas = re.findall(r"
(.*?)天
", response.text) 61 | data = f"{datas[0]} 天" if datas else "获取连续签到天数失败" 62 | msg += f"\n签到天数: {data}" 63 | 64 | msg = msg.strip() 65 | return msg 66 | 67 | def main(self): 68 | msg_all = "" 69 | for check_item in self.check_items: 70 | cookie = { 71 | item.split("=")[0]: item.split("=")[1] 72 | for item in check_item.get("cookie").split("; ") 73 | } 74 | 75 | session = requests.session() 76 | if check_item.get("proxy", ""): 77 | proxies = { 78 | "http": check_item.get("proxy", ""), 79 | "https": check_item.get("proxy", ""), 80 | } 81 | session.proxies.update(proxies) 82 | session.cookies.update(cookie) 83 | session.headers.update( 84 | { 85 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 86 | "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36", 87 | "referer": self.url, 88 | "accept": "text/html,application/xhtml+xml,application/xml;" 89 | "q=0.9,image/webp,image/apng,*/*;" 90 | "q=0.8,application/signed-exchange;v=b3;q=0.9", 91 | "accept-language": "zh-CN,zh;q=0.9,en;q=0.8", 92 | } 93 | ) 94 | 95 | msg = self.sign(session) 96 | msg_all += msg + "\n\n" 97 | return msg_all 98 | 99 | 100 | if __name__ == "__main__": 101 | _data = get_data() 102 | _check_items = _data.get("V2EX", []) 103 | result = V2ex(check_items=_check_items).main() 104 | send("V2EX", result) 105 | -------------------------------------------------------------------------------- /ck_weibo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 48 7 * * * 4 | new Env('微博'); 5 | """ 6 | 7 | from urllib.parse import parse_qsl, urlsplit 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class WeiBo: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def sign(token): 21 | headers = {"User-Agent": "Weibo/52588 (iPhone; iOS 14.5; Scale/3.00)"} 22 | res = requests.get( 23 | url=f"https://api.weibo.cn/2/checkin/add?c=iphone&{token}", headers=headers 24 | ).json() 25 | if res.get("status") == 10000: 26 | return ( 27 | f'连续签到: {res.get("data").get("continuous")}天\n' 28 | f'本次收益: {res.get("data").get("desc")}' 29 | ) 30 | 31 | if res.get("errno") == 30000: 32 | return "每日签到: 已签到" 33 | if res.get("status") == 90005: 34 | return f'每日签到: {res.get("msg")}' 35 | return "每日签到: 签到失败" 36 | 37 | @staticmethod 38 | def card(token): 39 | headers = {"User-Agent": "Weibo/52588 (iPhone; iOS 14.5; Scale/3.00)"} 40 | res = requests.get( 41 | url=f"https://api.weibo.cn/2/!/ug/king_act_home?c=iphone&{token}", 42 | headers=headers, 43 | ).json() 44 | if res.get("status") != 10000: 45 | return "每日打卡: 活动过期或失效" 46 | nickname = res.get("data").get("user").get("nickname") 47 | return ( 48 | f"用户昵称: {nickname}\n" 49 | f'每日打卡: {res.get("data").get("signin").get("title").split("<")[0]}天\n' 50 | f'积分总计: {res.get("data").get("user").get("energy")}' 51 | ) 52 | 53 | @staticmethod 54 | def pay(token): 55 | headers = { 56 | "Accept-Encoding": "gzip, deflate", 57 | "Connection": "keep-alive", 58 | "Content-Type": "application/x-www-form-urlencoded", 59 | "Host": "pay.sc.weibo.com", 60 | "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_5 like Mac OS X) " 61 | "AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 " 62 | "Weibo (iPhone10,1__weibo__11.2.1__iphone__os14.5)", 63 | } 64 | data = f"{token}&lang=zh_CN&wm=3333_2001" 65 | response = requests.post( 66 | url="https://pay.sc.weibo.com/aj/mobile/home/welfare/signin/do", 67 | headers=headers, 68 | data=data, 69 | ) 70 | try: 71 | res = response.json() 72 | if res.get("status") == 1: 73 | msg = f'微博钱包: {res.get("score")} 积分' 74 | elif res.get("status") == 2: 75 | msg = "微博钱包: 已签到" 76 | info_res = requests.post( 77 | url="https://pay.sc.weibo.com/api/client/sdk/app/balance", 78 | headers=headers, 79 | data=data, 80 | ).json() 81 | msg += f"\n当前现金: {info_res.get('data').get('balance')} 元" 82 | else: 83 | msg = "微博钱包: Cookie失效" 84 | return msg 85 | except Exception: 86 | msg = "微博钱包: Cookie失效" 87 | return msg 88 | 89 | def main(self): 90 | msg_all = "" 91 | for check_item in self.check_items: 92 | url = check_item.get("url") 93 | query_dict = dict(parse_qsl(urlsplit(url).query)) 94 | token = "&".join( 95 | [ 96 | f"{key}={value}" 97 | for key, value in query_dict.items() 98 | if key in ["from", "uid", "s", "gsid"] 99 | ] 100 | ) 101 | sign_msg = self.sign(token=token) 102 | card_msg = self.card(token=token) 103 | pay_msg = self.pay(token=token) 104 | msg = f"{sign_msg}\n{card_msg}\n{pay_msg}" 105 | msg_all += msg + "\n\n" 106 | return msg_all 107 | 108 | 109 | if __name__ == "__main__": 110 | _data = get_data() 111 | _check_items = _data.get("WEIBO", []) 112 | result = WeiBo(check_items=_check_items).main() 113 | send("微博", result) 114 | -------------------------------------------------------------------------------- /ck_wps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 12 6 * * * 4 | new Env('WPS'); 5 | """ 6 | 7 | import json 8 | import random 9 | import sys 10 | import time 11 | 12 | import requests 13 | 14 | from notify_mtr import send 15 | from utils import get_data 16 | 17 | 18 | class WPS: 19 | def __init__(self, check_items): 20 | self.check_items = check_items 21 | self.is_sign = False 22 | 23 | # 判断 Cookie 是否失效 和 今日是否签到 24 | def check(self, cookie): 25 | url0 = "https://vip.wps.cn/sign/mobile/v3/get_data" 26 | headers = { 27 | "Cookie": cookie, 28 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 29 | "AppleWebKit/537.36 (KHTML, like Gecko) " 30 | "Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586", 31 | } 32 | response = requests.get(url0, headers=headers) 33 | if "会员登录" in response.text: 34 | print("cookie 失效") 35 | sys.exit() 36 | is_sign = response.json().get("data", {}).get("is_sign") 37 | if is_sign: 38 | self.is_sign = True 39 | 40 | def sign(self, cookie): 41 | headers = { 42 | "Cookie": cookie, 43 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 44 | "AppleWebKit/537.36 (KHTML, like Gecko) " 45 | "Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586", 46 | } 47 | if self.is_sign: 48 | msg = "今日已签到" 49 | else: 50 | data0 = {"platform": "8"} # 不带验证坐标的请求 51 | url = "https://vip.wps.cn/sign/v2" 52 | response = requests.post(url, data0, headers=headers) 53 | if "msg" not in response.text: 54 | msg = "cookie 失效" 55 | else: 56 | sus = json.loads(response.text)["result"] 57 | msg = f"免验证签到 --> {sus}\n" 58 | if sus == "error": 59 | yz_url = ( 60 | "https://vip.wps.cn/checkcode/signin/captcha.png?" 61 | "platform=8&encode=0&img_witdh=275.164&img_height=69.184" 62 | ) 63 | data = { 64 | "platform": "8", 65 | "captcha_pos": "137.00431974731889, 36.00431593261568", 66 | "img_witdh": "275.164", 67 | "img_height": "69.184", 68 | } # 带验证坐标的请求 69 | for n in range(10): 70 | requests.get(yz_url, headers=headers) 71 | response = requests.post(url, data, headers=headers) 72 | sus = json.loads(response.text)["result"] 73 | msg += f"{str(n + 1)} 尝试验证签到 --> {sus}\n" 74 | time.sleep(random.randint(0, 5) / 10) 75 | if sus == "ok": 76 | break 77 | msg += f"最终签到结果 --> {sus}\n" 78 | # {"result":"ok","data":{"exp":0,"wealth":0,"weath_double":0,"count":5,"double":0,"gift_type":"space_5","gift_id":133,"url":""},"msg":""} 79 | return msg 80 | 81 | def main(self): 82 | msg_all = "" 83 | for check_item in self.check_items: 84 | cookie = check_item.get("cookie") 85 | self.check(cookie) 86 | msg = self.sign(cookie) 87 | msg_all += msg + "\n\n" 88 | return msg_all 89 | 90 | 91 | if __name__ == "__main__": 92 | _data = get_data() 93 | _check_items = _data.get("WPS", []) 94 | result = WPS(check_items=_check_items).main() 95 | send("WPS", result) 96 | -------------------------------------------------------------------------------- /ck_www2nzz.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 12 13 * * * 4 | new Env('咔叽网单'); 5 | """ 6 | 7 | import re 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class WWW2nzz: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def sign(session): 21 | response = session.get("http://www.2nzz.com/index.php", verify=False) 22 | formhash = re.findall( 23 | r'(.*?)", response.text, re.S) 51 | check_msg = check_msg[0].strip() if check_msg else "签到失败" 52 | 53 | return f"用户信息: {uid}\n签到信息: {check_msg}" 54 | 55 | def main(self): 56 | msg_all = "" 57 | for check_item in self.check_items: 58 | cookie = { 59 | item.split("=")[0]: item.split("=")[1] 60 | for item in check_item.get("cookie").split("; ") 61 | } 62 | session = requests.session() 63 | session.cookies.update(cookie) 64 | session.headers.update( 65 | { 66 | "Origin": "http://www.2nzz.com", 67 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " 68 | "AppleWebKit/537.36 (KHTML, like Gecko) " 69 | "Chrome/88.0.4324.182 Safari/537.36 Edg/88.0.705.74", 70 | "Accept": "text/html,application/xhtml+xml,application/xml;" 71 | "q=0.9,image/webp,image/apng,*/*;" 72 | "q=0.8,application/signed-exchange;v=b3;q=0.9", 73 | "Referer": "http://www.2nzz.com/index.php", 74 | "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8", 75 | } 76 | ) 77 | msg = self.sign(session) 78 | msg_all += msg + "\n\n" 79 | return msg_all 80 | 81 | 82 | if __name__ == "__main__": 83 | _data = get_data() 84 | _check_items = _data.get("WWW2NZZ", []) 85 | result = WWW2nzz(check_items=_check_items).main() 86 | send("咔叽网单", result) 87 | -------------------------------------------------------------------------------- /ck_wzyd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 00 8 * * * 4 | new Env('王者营地'); 5 | """ 6 | 7 | from urllib.parse import parse_qsl 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class WZYD: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def sign(data): 21 | res = requests.post( 22 | url="https://ssl.kohsocialapp.qq.com:10001/play/h5sign", data=data 23 | ).json() 24 | try: 25 | msg = "签到成功" if res["result"] == 0 else res["returnMsg"] 26 | except Exception: 27 | msg = "请求失败,请检查接口" 28 | return msg 29 | 30 | def main(self): 31 | msg_all = "" 32 | for check_item in self.check_items: 33 | data = dict(parse_qsl(check_item.get("data"))) 34 | try: 35 | user_id = data.get("userId", "") 36 | except Exception as e: 37 | print(f"获取用户信息失败: {e}") 38 | user_id = "未获取到用户信息" 39 | sign_msg = self.sign(data) 40 | msg = f"帐号信息: {user_id}\n签到信息: {sign_msg}" 41 | msg_all += msg + "\n\n" 42 | return msg_all 43 | 44 | 45 | if __name__ == "__main__": 46 | _data = get_data() 47 | _check_items = _data.get("WZYD", []) 48 | result = WZYD(check_items=_check_items).main() 49 | send("王者营地", result) 50 | -------------------------------------------------------------------------------- /ck_youdao.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 18 20 * * * 4 | new Env('有道云笔记'); 5 | """ 6 | 7 | import time 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class YouDao: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def get_space(cookie): 21 | url = "https://note.youdao.com/yws/mapi/user?method=get" 22 | headers = {"Cookie": cookie} 23 | res = requests.get(url=url, headers=headers).json() 24 | return 0 if res.get("q") is None else res.get("q") 25 | 26 | def sign(self, cookie): 27 | msg = f"签到前空间: {int(self.get_space(cookie))//1048576}M\n" 28 | ad = 0 29 | 30 | headers = {"Cookie": cookie} 31 | r = requests.get( 32 | "http://note.youdao.com/login/acc/pe/getsess?product=YNOTE", headers=headers 33 | ) 34 | c = "".join(f"{key}={value};" for key, value in r.cookies.items()) 35 | headers = {"Cookie": c} 36 | 37 | r2 = requests.post( 38 | "https://note.youdao.com/yws/api/daupromotion?method=sync", headers=headers 39 | ) 40 | if "error" not in r2.text: 41 | checkin1 = requests.post( 42 | "https://note.youdao.com/yws/mapi/user?method=checkin", headers=headers 43 | ) 44 | time.sleep(1) 45 | checkin2 = requests.post( 46 | "https://note.youdao.com/yws/mapi/user?method=checkin", 47 | {"device_type": "android"}, 48 | headers=headers, 49 | ) 50 | 51 | for _ in range(3): 52 | resp = requests.post( 53 | "https://note.youdao.com/yws/mapi/user?method=adRandomPrompt", 54 | headers=headers, 55 | ) 56 | ad += resp.json()["space"] // 1048576 57 | time.sleep(2) 58 | 59 | if "reward" in r2.text: 60 | s = self.get_space(cookie) 61 | msg += f"签到后空间: {int(s)//1048576}M\n" 62 | sync = r2.json()["rewardSpace"] // 1048576 63 | checkin_1 = checkin1.json()["space"] // 1048576 64 | checkin_2 = checkin2.json()["space"] // 1048576 65 | space = str(sync + checkin_1 + checkin_2 + ad) 66 | msg += f"获得空间:{space}M, 总空间:{int(s)//1048576}M" 67 | else: 68 | msg += f"错误 {str(r2.json())}" 69 | return msg 70 | 71 | def main(self): 72 | msg_all = "" 73 | for check_item in self.check_items: 74 | cookie = check_item.get("cookie") 75 | msg = self.sign(cookie) 76 | msg_all += msg + "\n\n" 77 | return msg_all 78 | 79 | 80 | if __name__ == "__main__": 81 | _data = get_data() 82 | _check_items = _data.get("YOUDAO", []) 83 | result = YouDao(check_items=_check_items).main() 84 | send("有道云笔记", result) 85 | -------------------------------------------------------------------------------- /ck_zhiyoo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | cron: 7 21 * * * 4 | new Env('智友邦'); 5 | """ 6 | 7 | import re 8 | 9 | import requests 10 | 11 | from notify_mtr import send 12 | from utils import get_data 13 | 14 | 15 | class Zhiyoo: 16 | def __init__(self, check_items): 17 | self.check_items = check_items 18 | 19 | @staticmethod 20 | def sign(session): 21 | response = session.get( 22 | url="http://bbs.zhiyoo.net/plugin.php?id=dsu_paulsign:sign", verify=False 23 | ) 24 | formhash = re.findall( 25 | r' q[ask/no], 3 | 'allow_installing_outdated_dists' => q[ask/no], 4 | 'applypatch' => q[], 5 | 'auto_commit' => q[0], 6 | 'build_cache' => q[100], 7 | 'build_dir' => q[/root/.cpan/build], 8 | 'build_dir_reuse' => q[0], 9 | 'build_requires_install_policy' => q[yes], 10 | 'bzip2' => q[/usr/bin/bzip2], 11 | 'cache_metadata' => q[1], 12 | 'check_sigs' => q[0], 13 | 'cleanup_after_install' => q[0], 14 | 'colorize_output' => q[0], 15 | 'commandnumber_in_prompt' => q[1], 16 | 'connect_to_internet_ok' => q[1], 17 | 'cpan_home' => q[/root/.cpan], 18 | 'ftp_passive' => q[1], 19 | 'ftp_proxy' => q[], 20 | 'getcwd' => q[cwd], 21 | 'gpg' => q[], 22 | 'gzip' => q[/bin/gzip], 23 | 'halt_on_failure' => q[0], 24 | 'histfile' => q[/root/.cpan/histfile], 25 | 'histsize' => q[100], 26 | 'http_proxy' => q[], 27 | 'inactivity_timeout' => q[0], 28 | 'index_expire' => q[1], 29 | 'inhibit_startup_message' => q[0], 30 | 'keep_source_where' => q[/root/.cpan/sources], 31 | 'load_module_verbosity' => q[none], 32 | 'make' => q[/usr/bin/make], 33 | 'make_arg' => q[], 34 | 'make_install_arg' => q[], 35 | 'make_install_make_command' => q[/usr/bin/make], 36 | 'makepl_arg' => q[], 37 | 'mbuild_arg' => q[], 38 | 'mbuild_install_arg' => q[], 39 | 'mbuild_install_build_command' => q[./Build], 40 | 'mbuildpl_arg' => q[], 41 | 'no_proxy' => q[], 42 | 'pager' => q[/usr/bin/less], 43 | 'patch' => q[], 44 | 'perl5lib_verbosity' => q[none], 45 | 'prefer_external_tar' => q[1], 46 | 'prefer_installer' => q[MB], 47 | 'prefs_dir' => q[/root/.cpan/prefs], 48 | 'prerequisites_policy' => q[follow], 49 | 'recommends_policy' => q[1], 50 | 'scan_cache' => q[atstart], 51 | 'shell' => q[/bin/bash], 52 | 'show_unparsable_versions' => q[0], 53 | 'show_upload_date' => q[0], 54 | 'show_zero_versions' => q[0], 55 | 'suggests_policy' => q[0], 56 | 'tar' => q[/bin/tar], 57 | 'tar_verbosity' => q[none], 58 | 'term_is_latin' => q[1], 59 | 'term_ornaments' => q[1], 60 | 'test_report' => q[0], 61 | 'trust_test_report_history' => q[0], 62 | 'unzip' => q[/usr/bin/unzip], 63 | 'urllist' => [q[http://mirrors.ustc.edu.cn/CPAN/]], 64 | 'use_prompt_default' => q[0], 65 | 'use_sqlite' => q[0], 66 | 'version_timeout' => q[15], 67 | 'wget' => q[/usr/bin/wget], 68 | 'yaml_load_code' => q[0], 69 | 'yaml_module' => q[YAML], 70 | }; 71 | 1; 72 | __END__ 73 | -------------------------------------------------------------------------------- /env.sample: -------------------------------------------------------------------------------- 1 | # 打印日志 2 | 3 | # 0. 执行任务时是否显示签到详情,1 为显示,0 为不显示,默认为 1 4 | DISPLAY_CONTEXT=1 5 | 6 | 7 | # 推送方式 8 | 9 | # 1. 钉钉机器人 10 | DD_BOT_TOKEN="" 11 | 12 | # 2. Server 酱 13 | PUSH_KEY="" 14 | # Turbo 版 15 | PUSH_TURBO_KEY="" 16 | 17 | # 3. PUSHPLUS 18 | PUSH_PLUS_TOKEN="" 19 | 20 | # 4. Qmsg 酱 21 | QMSG_KEY="" 22 | 23 | # 5. 企业微信 24 | # 企业微信 ID 25 | CORPID="" 26 | # 企业微信应用 ID 27 | AGENTID="" 28 | # 企业微信密钥 29 | CORPSECRET="" 30 | 31 | # 6. TelegramBot 32 | TG_BOT_TOKEN="" 33 | TG_USER_ID="" 34 | 35 | 36 | # 信息配置 37 | 38 | # 1. SSPanel 签到 39 | # 用户信息。格式:域名----账号----密码,多个账号使用 ; 分隔,支持换行但前后引号不能删掉 40 | SS_USERS="https://abc.com----abc@abc.com----abc123456; 41 | https://abc.com----abc@abc.com----abc123456; 42 | https://abc.com----abc@abc.com----abc123456;" -------------------------------------------------------------------------------- /ins_pkg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # shellcheck disable=SC2005,2188 4 | <<'COMMENT' 5 | cron: 16 */2 * * * 6 | new Env('签到依赖'); 7 | COMMENT 8 | 9 | . utils_env.sh 10 | get_some_path 11 | 12 | alpine_pkgs="bash curl gcc git jq libffi-dev make musl-dev openssl-dev perl perl-app-cpanminus perl-dev py3-pip python3 python3-dev wget" 13 | py_reqs="bs4 cryptography dateparser feedparser peewee pyaes pyppeteer requests rsa schedule tomli" 14 | js_pkgs="@iarna/toml axios cron-parser crypto-js got" 15 | pl_mods="File::Slurp JSON5 TOML::Dumper" 16 | 17 | install() { 18 | count=0 19 | flag=$1 20 | while true; do 21 | echo ".......... $2 begin .........." 22 | result=$3 23 | if [ "$result" -gt 0 ]; then 24 | flag=0 25 | else 26 | flag=1 27 | fi 28 | if [ $flag -eq "$1" ]; then 29 | echo "---------- $2 succeed ----------" 30 | break 31 | else 32 | count=$((count + 1)) 33 | if [ $count -eq 6 ]; then 34 | echo "!! 自动安装失败,请尝试进入容器后执行 $2 !!" 35 | break 36 | fi 37 | echo ".......... retry in 5 seconds .........." 38 | sleep 5 39 | fi 40 | done 41 | } 42 | 43 | install_alpine_pkgs() { 44 | apk update 45 | apk_info=" $(apk info) " 46 | for i in $alpine_pkgs; do 47 | if expr "$apk_info" : ".*\s${i}\s.*" >/dev/null; then 48 | echo "$i 已安装" 49 | else 50 | install 0 "apk add $i" "$(apk add --no-cache "$i" | grep -c 'OK')" 51 | fi 52 | done 53 | } 54 | 55 | install_py_reqs() { 56 | pip3 install --upgrade pip 57 | pip3_freeze="$(pip3 freeze)" 58 | for i in $py_reqs; do 59 | if expr "$pip3_freeze" : ".*${i}" >/dev/null; then 60 | echo "$i 已安装" 61 | else 62 | install 0 "pip3 install $i" "$(pip3 install "$i" | grep -c 'Successfully')" 63 | fi 64 | done 65 | } 66 | 67 | install_js_pkgs_initial() { 68 | if [ -d "${SCR_PATH}/OreosLab_checkinpanel_master" ]; then 69 | cd "${SCR_PATH}/OreosLab_checkinpanel_master" && 70 | cp "${REPO_PATH}/OreosLab_checkinpanel_master/package.json" "${SCR_PATH}/OreosLab_checkinpanel_master/package.json" 71 | elif [ -d "/ql/scripts" ] && [ ! -f "/ql/scripts/package.bak.json" ]; then 72 | cd /ql/scripts || exit 73 | rm -rf node_modules 74 | rm -rf .pnpm-store 75 | mv package-lock.json package-lock.bak.json 76 | mv package.json package.bak.json 77 | mv pnpm-lock.yaml pnpm-lock.bak.yaml 78 | install 1 "npm install -g package-merge" "$(echo "$(npm install -g package-merge && npm ls -g package-merge)" | grep -cE '(empty)|ERR')" && 79 | export NODE_PATH="/usr/local/lib/node_modules" && 80 | node -e \ 81 | "const merge = require('package-merge'); 82 | const fs = require('fs'); 83 | const dst = fs.readFileSync('/ql/repo/OreosLab_checkinpanel_master/package.json'); 84 | const src = fs.readFileSync('/ql/scripts/package.bak.json'); 85 | fs.writeFile('/ql/scripts/package.json', merge(dst, src), function (err) { 86 | if (err) { 87 | console.log(err); 88 | } 89 | console.log('package.json merged successfully!'); 90 | });" 91 | fi 92 | npm install 93 | } 94 | install_js_pkgs_each() { 95 | is_empty=$(npm ls "$1" | grep empty) 96 | has_err=$(npm ls "$1" | grep ERR) 97 | if [ "$is_empty" = "" ] && [ "$has_err" = "" ]; then 98 | echo "$1 已正确安装" 99 | elif [ "$has_err" != "" ]; then 100 | uninstall_js_pkgs "$1" 101 | else 102 | install 1 "npm install $1" "$(echo "$(npm install --force "$1" && npm ls --force "$1")" | grep -cE '(empty)|ERR')" 103 | fi 104 | } 105 | uninstall_js_pkgs() { 106 | npm uninstall "$1" 107 | rm -rf "$(pwd)"/node_modules/"$1" 108 | rm -rf /usr/local/lib/node_modules/lodash/* 109 | npm cache clear --force 110 | } 111 | install_js_pkgs_all() { 112 | install_js_pkgs_initial 113 | for i in $js_pkgs; do 114 | install_js_pkgs_each "$i" 115 | done 116 | npm ls --depth 0 117 | } 118 | 119 | install_pl_mods() { 120 | if command -v cpm >/dev/null 2>&1; then 121 | echo "App::cpm 已安装" 122 | else 123 | install 1 "cpanm -fn App::cpm" "$(cpanm -fn App::cpm | grep -c "FAIL")" 124 | if ! command -v cpm >/dev/null 2>&1; then 125 | if [ -f ./cpm ]; then 126 | chmod +x cpm && ./cpm --version 127 | else 128 | cp -f /ql/repo/OreosLab_checkinpanel_master/cpm ./ && chmod +x cpm && ./cpm --version 129 | if [ ! -f ./cpm ]; then 130 | curl -fsSL https://cdn.jsdelivr.net/gh/Oreomeow/checkinpanel/cpm >cpm && chmod +x cpm && ./cpm --version 131 | fi 132 | fi 133 | fi 134 | fi 135 | for i in $pl_mods; do 136 | if [ -f "$(perldoc -l "$i")" ]; then 137 | echo "$i 已安装" 138 | else 139 | install 1 "cpm install -g $i" "$(cpm install -g "$i" | grep -c "FAIL")" 140 | fi 141 | done 142 | } 143 | 144 | install_alpine_pkgs 145 | install_py_reqs 146 | install_js_pkgs_all 147 | install_pl_mods 148 | -------------------------------------------------------------------------------- /ins_selenium.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 4 | 5 | alpine_pkgs="bash curl gcc git jq libffi-dev make musl-dev openssl-dev py3-pip python3 python3-dev wget" 6 | py_reqs="cryptography selenium PyVirtualDisplay" 7 | chromium_pkgs="chromium libexif eudev" 8 | chromedriver_pkgs="chromium-chromedriver" 9 | xvfb_pkgs="xvfb" 10 | alpine_pkgs="$alpine_pkgs $chromium_pkgs $chromedriver_pkgs $xvfb_pkgs" 11 | 12 | install() { 13 | count=0 14 | flag=$1 15 | while true; do 16 | echo ".......... $2 begin .........." 17 | result=$3 18 | if [ "$result" -gt 0 ]; then 19 | flag=0 20 | else 21 | flag=1 22 | fi 23 | if [ $flag -eq "$1" ]; then 24 | echo "---------- $2 succeed ----------" 25 | break 26 | else 27 | count=$((count + 1)) 28 | if [ $count -eq 6 ]; then 29 | echo "!! 自动安装失败,请尝试进入容器后执行 $2 !!" 30 | break 31 | fi 32 | echo ".......... retry in 5 seconds .........." 33 | sleep 5 34 | fi 35 | done 36 | } 37 | 38 | install_alpine_pkgs() { 39 | apk update 40 | apk_info=" $(apk info) " 41 | for i in $alpine_pkgs; do 42 | if expr "$apk_info" : ".*\s${i}\s.*" >/dev/null; then 43 | echo "$i 已安装" 44 | else 45 | install 0 "apk add $i" "$(apk add --no-cache "$i" | grep -c 'OK')" 46 | fi 47 | done 48 | } 49 | 50 | install_py_reqs() { 51 | pip3 install --upgrade pip 52 | pip3_freeze="$(pip3 freeze)" 53 | for i in $py_reqs; do 54 | if expr "$pip3_freeze" : ".*${i}" >/dev/null; then 55 | echo "$i 已安装" 56 | else 57 | install 0 "pip3 install $i" "$(pip3 install "$i" | grep -c 'Successfully')" 58 | fi 59 | done 60 | } 61 | 62 | install_alpine_pkgs 63 | install_py_reqs 64 | -------------------------------------------------------------------------------- /notify.sample.toml: -------------------------------------------------------------------------------- 1 | # 通知配置文件 2 | 3 | # bark 服务 4 | ## bark 推送是否存档,可不填 5 | BARK_ARCHIVE = "" 6 | ## bark 推送分组,可不填 7 | BARK_GROUP = "" 8 | ## bark IP 或设备码,例:DxHcxxxxxRxxxxxxcm 或 https://api.example.com/DxHcxxxxxRxxxxxxcm 9 | BARK_PUSH = "" 10 | ## bark 推送声音,可不填 11 | BARK_SOUND = "" 12 | 13 | # 控制台输出 14 | ## true 为开启,false 为关闭,默认关闭 15 | CONSOLE = true 16 | 17 | # 钉钉机器人 18 | ## 【必填】 19 | DD_BOT_SECRET = "" 20 | ## 【必填】 21 | DD_BOT_TOKEN = "" 22 | 23 | # 飞书机器人 24 | ## 【必填】 25 | FSKEY = "" 26 | 27 | # go-cqhttp 28 | ## GOBOT_URL 设置 /send_private_msg 时填入 user_id=个人QQ 29 | ## 设置 /send_group_msg 时填入 group_id=QQ群 30 | GOBOT_QQ = "" 31 | ## go-cqhttp 的 access_token,可不填 32 | GOBOT_TOKEN = "" 33 | ## 推送到个人QQ: http://127.0.0.1/send_private_msg 34 | ## 群: http://127.0.0.1/send_group_msg 35 | GOBOT_URL = "" 36 | 37 | # 一言 38 | ## true 为开启,false 为关闭,默认关闭 39 | HITOKOTO = true 40 | 41 | # iGot 聚合推送 42 | ## 【必填】 43 | IGOT_PUSH_KEY = "" 44 | 45 | # server 酱 46 | ## 兼容旧版和 Turbo 版 47 | PUSH_KEY = "" 48 | 49 | # push+ 微信推送 50 | ## 【必填】 51 | PUSH_PLUS_TOKEN = "" 52 | ## 群组编码,不填仅推送自己 53 | PUSH_PLUS_USER = "" 54 | 55 | # qmsg 酱 56 | ## 【必填】 57 | QMSG_KEY = "" 58 | ## 推送到群填写 group,默认推送到 QQ 59 | QMSG_TYPE = "" 60 | 61 | # 企业微信应用 62 | ## 参考 http://note.youdao.com/s/HMiudGkb 63 | ## 依次填入 corpid, corpsecret, touser(注:多个成员ID使用|隔开), agentid, media_id(选填,不填默认文本消息类型) 64 | QYWX_AM = "" 65 | 66 | # 企业微信机器人 67 | QYWX_KEY = "" 68 | 69 | # tg 机器人 70 | ## tg api 自建反向代理地址,默认 api.telegram.org,可不填 71 | TG_API_HOST = "" 72 | ## 【必填】 73 | TG_BOT_TOKEN = "" 74 | ## tg 代理配置认证参数,可不填 75 | TG_PROXY_AUTH = "" 76 | ## 例:127.0.0.1,可不填 77 | TG_PROXY_HOST = "" 78 | ## 例:1080,可不填 79 | TG_PROXY_PORT = "" 80 | ## 【必填】 81 | TG_USER_ID = "" 82 | -------------------------------------------------------------------------------- /notify.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # shellcheck disable=SC2154 4 | send_message() { 5 | echo -e "\n通知结果:" 6 | 7 | # 钉钉群机器人通知 8 | if [ "${DD_BOT_TOKEN}" ]; then 9 | push=$( 10 | curl -k -s "https://oapi.dingtalk.com/robot/send?access_token=${DD_BOT_TOKEN}" \ 11 | -H 'Content-Type:application/json' \ 12 | -d "{ 13 | \"msgtype\":\"markdown\", 14 | \"markdown\":{ 15 | \"title\":\"${TITLE}\", 16 | \"text\":\"${log_text}\" 17 | } 18 | }" 19 | ) 20 | push_code=$(echo "${push}" | jq -r ".errcode" 2>&1) 21 | if [ "${push_code}" -eq 0 ]; then 22 | echo -e "钉钉机器人推送结果:成功" 23 | else 24 | echo -e "钉钉机器人推送结果:失败" 25 | fi 26 | fi 27 | 28 | # Server 酱通知 29 | if [ "${PUSH_KEY}" ]; then 30 | echo -e "text=${TITLE}&desp=${log_text}" >"${PUSH_TMP_PATH}" 31 | push=$(curl -k -s --data-binary @"${PUSH_TMP_PATH}" "https://sc.ftqq.com/${PUSH_KEY}.send") 32 | push_code=$(echo "${push}" | jq -r ".errno" 2>&1) 33 | if [ "${push_code}" -eq 0 ]; then 34 | echo -e "Server 酱推送结果:成功" 35 | else 36 | echo -e "Server 酱推送结果:失败" 37 | fi 38 | fi 39 | 40 | # Server 酱 Turbo 通知 41 | if [ "${PUSH_TURBO_KEY}" ]; then 42 | echo -e "text=${TITLE}&desp=${log_text}" >"${PUSH_TMP_PATH}" 43 | push=$(curl -k -s -X POST --data-binary @"${PUSH_TMP_PATH}" "https://sctapi.ftqq.com/${PUSH_TURBO_KEY}.send") 44 | ############################### 45 | # push 成功后,获取相关查询参数 46 | ############################### 47 | 48 | push_code=$(echo "${push}" | jq -r ".data.errno" 2>&1) 49 | push_id=$(echo "${push}" | jq -r ".data.pushid" 2>&1) 50 | push_readkey=$(echo "${push}" | jq -r ".data.readkey" 2>&1) 51 | 52 | ######################################################## 53 | # 企业微信推送逻辑修改 54 | # 先放入队列,push_code 为 0 代表放入队列成功不代表推送成功 55 | ######################################################## 56 | 57 | if [ "${push_code}" -eq 0 ]; then 58 | echo -e "Server 酱 Turbo 队列结果:成功" 59 | 60 | ############################################ 61 | # 推送结果需要异步查询 62 | # 目前每隔两秒查询一次,轮询 10 次检查推送结果 63 | ############################################ 64 | 65 | i=1 66 | while [ $i -le 10 ]; do 67 | wx_status=$(curl -s "https://sctapi.ftqq.com/push?id=${push_id}&readkey=${push_readkey}") 68 | wx_result=$(echo "${wx_status}" | jq -r ".data.wxstatus" 2>&1 | sed 's/\"{/{/g' | sed 's/\}"/}/g' | sed 's/\\"/"/g') 69 | if [ "${wx_result}" ]; then 70 | wx_errcode=$(echo "${wx_result}" | jq -r ".errcode" 2>&1) 71 | if [ "${wx_errcode}" -eq 0 ]; then 72 | echo -e "Server 酱 Turbo 推送结果:成功" 73 | else 74 | echo -e "Server 酱 Turbo 推送结果:失败,错误码:${wx_errcode}, more info at https:\\open.work.weixin.qq.com\devtool" 75 | fi 76 | break 77 | else 78 | if [ $i -lt 10 ]; then 79 | ((i++)) || true 80 | Sleep 2s 81 | else 82 | echo -e "Server 酱Turbo 推送结果:检查超时,请自行确认结果" 83 | fi 84 | 85 | fi 86 | 87 | done 88 | else 89 | echo -e "Server 酱Turbo 队列结果:失败" 90 | fi 91 | fi 92 | 93 | # PushPlus 通知 94 | if [ "${PUSH_PLUS_TOKEN}" ]; then 95 | echo -e "token=${PUSH_PLUS_TOKEN}&title=${TITLE}&content=${log_text}" >"${PUSH_TMP_PATH}" 96 | push=$(curl -k -s --data-binary @"${PUSH_TMP_PATH}" "http://www.pushplus.plus/send") 97 | push_code=$(echo "${push}" | jq -r ".code" 2>&1) 98 | if [ "${push_code}" -eq 200 ]; then 99 | echo -e "PushPlus 推送结果:成功" 100 | else 101 | push=$(curl -k -s --data-binary @"${PUSH_TMP_PATH}" "http://pushplus.hxtrip.com/send") 102 | push_code=$(echo "${push}" | jq -r ".code" 2>&1) 103 | if [ "${push_code}" -eq 200 ]; then 104 | echo -e "PushPlus(hxtrip) 推送结果:成功" 105 | else 106 | echo -e "PushPlus 推送结果:失败" 107 | fi 108 | fi 109 | fi 110 | 111 | # Qmsg 酱通知 112 | if [ "${QMSG_KEY}" ]; then 113 | result_qmsg_log_text="${TITLE}${log_text}" 114 | echo -e "msg=${result_qmsg_log_text}" >"${PUSH_TMP_PATH}" 115 | push=$(curl -k -s --data-binary @"${PUSH_TMP_PATH}" "https://qmsg.zendee.cn/send/${QMSG_KEY}") 116 | push_code=$(echo "${push}" | jq -r ".success" 2>&1) 117 | if [ "${push_code}" = "true" ]; then 118 | echo -e "Qmsg 酱推送结果:成功" 119 | else 120 | echo -e "Qmsg 酱推送结果:失败" 121 | fi 122 | fi 123 | 124 | # 企业微信通知 125 | if [ "${CORPID}" ] && [ "${AGENTID}" ] && [ "${CORPSECRET}" ]; then 126 | # 获取 token 127 | token=$(curl -k -s -G "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${CORPID}&corpsecret=${CORPSECRET}") 128 | access_token=$(echo "${token}" | jq -r ".access_token" 2>&1) 129 | 130 | if [ "${access_token}" ]; then 131 | result_wework_log_text="${TITLE}${log_text}" 132 | push=$( 133 | curl -k -s "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${access_token}" \ 134 | -H 'Content-Type:application/json' \ 135 | -d "{ 136 | \"touser\":\"@all\", 137 | \"msgtype\":\"text\", 138 | \"agentid\":\"${AGENTID}\", 139 | \"text\":{ 140 | \"content\":\"${result_wework_log_text}\" 141 | } 142 | }" 143 | ) 144 | push_code=$(echo "${push}" | jq -r ".errcode" 2>&1) 145 | if [ "${push_code}" -eq 0 ]; then 146 | echo -e "企业微信推送结果:成功" 147 | else 148 | echo -e "企业微信推送结果:失败" 149 | fi 150 | else 151 | echo -e "企业微信推送结果:失败 原因:token 获取失败" 152 | fi 153 | fi 154 | 155 | # push.jwks123.com 通知 156 | if [ "${SRE_TOKEN}" ]; then 157 | result_sre24_log_text="${TITLE}${log_text}" 158 | push=$( 159 | curl -k -sL https://push.jwks123.com/to/ \ 160 | -d "{\"token\":\"${token}\",\"msg\":\"${result_sre24_log_text}\"}" 161 | ) 162 | push_code=$(echo "${push}" | jq -r ".code" 2>&1) 163 | if [ "${push_code}" -eq 202 ]; then 164 | echo -e "push.jwks123.com 推送结果:成功" 165 | else 166 | echo -e "push.jwks123.com 推送结果:失败" 167 | fi 168 | fi 169 | 170 | # TelegramBot 通知 171 | if [ "${TG_BOT_TOKEN}" ] && [ "${TG_USER_ID}" ]; then 172 | result_tgbot_log_text="${TITLE}${log_text}" 173 | echo -e "chat_id=${TG_USER_ID}&parse_mode=HTML&text=${result_tgbot_log_text}" >"${PUSH_TMP_PATH}" 174 | push=$(curl -k -s --data-binary @"${PUSH_TMP_PATH}" "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage") 175 | push_code=$(echo "${push}" | grep -o '"ok":true') 176 | if [ "${push_code}" ]; then 177 | echo -e "TelegramBot 推送结果:成功" 178 | else 179 | echo -e "TelegramBot 推送结果:失败" 180 | fi 181 | fi 182 | } 183 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@iarna/toml": "^2.2.5", 4 | "axios": "^0.23.0", 5 | "cron-parser": "^4.6.0", 6 | "crypto-js": "^4.1.1", 7 | "got": "<=11.8.6", 8 | "request": "^2.88.2" 9 | }, 10 | "name": "checkinpanel", 11 | "description": "
\r

定时面板上的签到盒

", 12 | "version": "4.0.0", 13 | "main": "checkinjs.js", 14 | "devDependencies": {}, 15 | "scripts": { 16 | "test": "echo \"Error: no test specified\" && exit 1" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/Oreomeow/checkinpanel.git" 21 | }, 22 | "author": "Oreomeow", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/Oreomeow/checkinpanel/issues" 26 | }, 27 | "homepage": "https://github.com/Oreomeow/checkinpanel#readme" 28 | } 29 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | target-version = ["py38"] 3 | line-length = 88 4 | skip-magic-trailing-comma = true 5 | 6 | [tool.isort] 7 | profile = "black" 8 | combine_as_imports = true 9 | multi_line_output = 3 10 | 11 | [tool.pylint.master] 12 | py-version = "3.8" 13 | ignore = [] 14 | jobs = 2 15 | suggestion-mode = "yes" 16 | 17 | [tool.pylint.basic] 18 | argument-naming-style = "any" 19 | variable-naming-style = "any" 20 | 21 | [tool.pylint.format] 22 | expected-line-ending-format = "LF" 23 | max-line-length = 120 24 | 25 | [tool.pylint.design] 26 | min-public-methods = 0 27 | 28 | [tool.pylint.message_control] 29 | disable = [ 30 | "broad-except", 31 | "consider-using-with", 32 | "import-outside-toplevel", 33 | "missing-docstring", 34 | "pointless-string-statement", 35 | ] 36 | 37 | [tool.mypy] 38 | python_version = 3.8 39 | exclude = ["build|dist|venv"] 40 | ignore_missing_imports = true 41 | show_error_codes = true 42 | 43 | [[tool.mypy.overrides]] 44 | module = [] 45 | ignore_errors = true 46 | -------------------------------------------------------------------------------- /rss.sample.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OreosLab/checkinpanel/911402e4b2e559ffd62c4949a971ddd6834edd7d/rss.sample.db -------------------------------------------------------------------------------- /rss.sample.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO rss (id, feed, title, url, before) VALUES (1, 'https://rsshub.app/telegram/channel/Oreomsg', '猫粮投喂', 'https://t.me/Oreomsg', 1); 2 | INSERT INTO rss (id, feed, title, url, before) VALUES (2, 'https://rsshub.app/telegram/channel/NewlearnerChannel', 'Newlearnerの自留地', 'https://t.me/NewlearnerChannel', 1); 3 | INSERT INTO rss (id, feed, title, url, before) VALUES (3, 'https://rsshub.app/telegram/channel/appinnfeed', 'Appinn Feed', 'https://t.me/appinnfeed', 1); 4 | INSERT INTO rss (id, feed, title, url, before) VALUES (4, 'https://rsshub.app/zhihu/daily', '知乎日报', 'https://daily.zhihu.com', 1); 5 | INSERT INTO rss (id, feed, title, url, before) VALUES (5, 'https://www.zhihu.com/rss', '知乎', 'https://www.zhihu.com', 1); 6 | INSERT INTO rss (id, feed, title, url, before) VALUES (6, 'https://www.ruanyifeng.com/blog/atom.xml', '阮一峰的网络日志', 'https://www.ruanyifeng.com/blog', 1); 7 | INSERT INTO rss (id, feed, title, url, before) VALUES (7, 'https://rsshub.app/zhihu/hotlist', '知乎热榜', 'https://www.zhihu.com', 1); 8 | INSERT INTO rss (id, feed, title, url, before) VALUES (8, 'https://rsshub.app/blogs/foreverblog', '十年之约', 'https://www.foreverblog.cn', 1); 9 | INSERT INTO rss (id, feed, title, url, before) VALUES (9, 'https://rsshub.app/bjp/apod', '北京天文馆每日一图', 'https://www.bjp.org.cn/', 1); 10 | INSERT INTO rss (id, feed, title, url, before) VALUES (10, 'https://www.williamlong.info/rss.xml', '月光博客', 'https://www.williamlong.info', 1); 11 | INSERT INTO rss (id, feed, title, url, before) VALUES (11, 'https://insights.thoughtworks.cn/feed/', 'Thoughtworks洞见', 'https://insights.thoughtworks.cn', 1); 12 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import sys 5 | import traceback 6 | 7 | import tomli 8 | 9 | import utils_env 10 | from utils_ver import print_ver 11 | 12 | # 缓存全局的环境 13 | DATA: dict = {} 14 | 15 | 16 | def get_data() -> dict: 17 | """ 18 | 获取签到的配置文件。 19 | 20 | :return: 签到配置文件对象 21 | """ 22 | print_ver() 23 | global DATA 24 | if DATA: 25 | return DATA 26 | 27 | if check_config := os.getenv("CHECK_CONFIG"): 28 | if not os.path.exists(check_config): 29 | print(f"错误:环境变量 CHECK_CONFIG 指定的配置文件 {check_config} 不存在!") 30 | sys.exit(1) 31 | else: 32 | check_config = utils_env.get_file_path("check.toml") 33 | if not check_config: 34 | print("错误:未检查到签到配置文件,请在指定位置创建文件或设置 CHECK_CONFIG 指定你的文件。") 35 | sys.exit(1) 36 | 37 | try: 38 | DATA = tomli.load(open(check_config, "rb")) 39 | return DATA 40 | except tomli.TOMLDecodeError: 41 | print( 42 | f"错误:配置文件 {check_config} 格式不对,请学习 https://toml.io/cn/v1.0.0\n错误信息:\n{traceback.format_exc()}" 43 | ) 44 | sys.exit(1) 45 | -------------------------------------------------------------------------------- /utils_env.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import platform 5 | 6 | # 缓存全局的环境 7 | ENV = "" 8 | 9 | 10 | def get_env_str() -> str: 11 | """ 12 | 尝试获取当前系统的环境,返回字符。 13 | 14 | :return: Windows / Linux / Darwin / github / v2p / ql_new / ql /空 15 | """ 16 | global ENV 17 | if ENV: 18 | return ENV 19 | 20 | v2p_file = "/usr/local/app/script/Lists/task.list" 21 | ql_new_file = "/ql/data/config/env.sh" 22 | ql_file = "/ql/config/env.sh" 23 | 24 | print("尝试检查运行环境...") 25 | if os.getenv("GITHUB_ACTIONS"): 26 | print("成功,当前环境为: github action 面板。") 27 | env = "github" 28 | elif os.path.exists(v2p_file): 29 | print("成功,当前环境为: elecV2P 面板。") 30 | env = "v2p" 31 | elif os.path.exists(ql_new_file): 32 | print("成功,当前环境为: 青龙面板(v2.12.0+)。") 33 | env = "ql_new" 34 | elif os.path.exists(ql_file): 35 | print("成功,当前环境为: 青龙面板。") 36 | env = "ql" 37 | 38 | # 面板判断优先于系统判断 39 | elif (e := platform.system()) == "Windows" or "Linux" or "Darwin": 40 | print(f"成功,当前环境为 {e}。") 41 | env = e 42 | else: 43 | print("失败!请检查环境。") 44 | env = "" 45 | 46 | ENV = env 47 | print("环境检查结束。\n") 48 | return env 49 | 50 | 51 | def get_env_int() -> int: 52 | """ 53 | 尝试获取当前系统的环境,返回数字。 54 | 55 | :return: 空: -1 / Windows: 0 / Linux: 1 / Darwin: 2 / github: 3 / v2p: 4 / ql_new: 5 / ql: 6 56 | """ 57 | env = get_env_str() 58 | if env == "Windows": 59 | return 0 60 | if env == "Linux": 61 | return 1 62 | if env == "Darwin": 63 | return 2 64 | if env == "github": 65 | return 3 66 | if env == "v2p": 67 | return 4 68 | if env == "ql_new": 69 | return 5 70 | return 6 if env == "ql" else -1 71 | 72 | 73 | def get_file_path(file_name: str) -> str: 74 | """ 75 | 根据文件名返回对应环境中位置。 76 | 77 | :param file_name: 文件名,不含任何 "/" (即目录) 78 | :return: 如果有面板,返回面板默认配置文件夹,否则返回当前目录下文件。
如果路径下文件不存在,返回空串。 79 | """ 80 | env_i = get_env_int() 81 | print(f"配置文件 ({file_name}) 检查开始...") 82 | paths = [ 83 | file_name, 84 | file_name, 85 | file_name, 86 | file_name, 87 | f"/usr/local/app/script/Lists/{file_name}", 88 | f"/ql/data/config/{file_name}", 89 | f"/ql/config/{file_name}", 90 | ] 91 | 92 | if env_i < 0: 93 | print("无法判断环境,选择当前目录为配置文件夹目录。") 94 | env_i = 0 95 | if not os.path.exists(paths[env_i]): 96 | print(f"未找到配置文件(不一定是错误),路径为: {paths[env_i]}。") 97 | print("配置文件检查结束。\n") 98 | return "" 99 | print(f"在 {paths[env_i]} 发现配置文件。") 100 | print("配置文件检查结束。\n") 101 | return paths[env_i] 102 | -------------------------------------------------------------------------------- /utils_env.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 4 | 5 | # 初始化变量 6 | V2P_FILE='/usr/local/app/script/Lists/task.list' 7 | QL_FILE='/ql/config/env.sh' 8 | QL_NEW_FILE='/ql/data/config/env.sh' 9 | IS_DISPLAY_CONTEXT=1 10 | 11 | # 检查环境:面板先于系统 12 | # shellcheck disable=SC2034 13 | check_env() { 14 | if [ -f "${V2P_FILE}" ]; then 15 | panel="elecv2p" 16 | elif [ -f "${QL_NEW_FILE}" ]; then 17 | panel="qinglong_new" 18 | elif [ -f "${QL_FILE}" ]; then 19 | panel="qinglong" 20 | else 21 | CMD="$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2) 22 | $(hostnamectl 2>/dev/null | grep -i system | cut -d : -f2) 23 | $(lsb_release -ds 2>/dev/null) 24 | $(grep -i description /etc/lsb-release 2>/dev/null | cut -d \" -f2) 25 | $(grep . /etc/redhat-release 2>/dev/null) 26 | $(grep . /etc/issue 2>/dev/null | cut -d \\ -f1 | sed '/^[ ]*$/d') 27 | $(uname -a 2>/dev/null)" 28 | 29 | REGEX="debian ubuntu centos|kernel|oracle[[:space:]]*linux|alma|rocky amazon[[:space:]]*linux alpine darwin nas" 30 | RELEASE="debian ubuntu centos centos alpine macos nas" 31 | CO="0 0 0 amazon 0 0 0" 32 | 33 | for i in $(echo "$CMD" | tr -d ' ' | tr '\n' ' '); do 34 | sys="$i" && [ -n "$sys" ] && break 35 | done 36 | 37 | count1=0 38 | for a in $REGEX; do 39 | count1=$((count1 + 1)) 40 | count2=0 41 | for b in $RELEASE; do 42 | count2=$((count2 + 1)) 43 | [ $count2 -eq $count1 ] || continue 44 | count3=0 45 | for c in $CO; do 46 | count3=$((count3 + 1)) 47 | [ $count3 -eq $count2 ] || continue 48 | echo "$sys" | grep -Eiq "$a" && system="$b" && company="$c" && [ -n "$system" ] || break 2 && break 3 49 | done 50 | done 51 | done 52 | 53 | fi 54 | } 55 | 56 | # 获取配置 57 | # shellcheck disable=SC2034 58 | source_config() { 59 | check_env 60 | if [ "${ENV_PATH}" ]; then 61 | ENV_FILE=$ENV_PATH 62 | elif [ "${panel}" = "elecv2p" ]; then 63 | ENV_FILE="/usr/local/app/script/Lists/.env" 64 | elif [ "${panel}" = "qinglong_new" ]; then 65 | ENV_FILE="/ql/data/config/.env" 66 | elif [ "${panel}" = "qinglong" ]; then 67 | ENV_FILE="/ql/config/.env" 68 | else 69 | ENV_FILE=".env" 70 | fi 71 | 72 | if [ -f "${ENV_FILE}" ]; then 73 | . "${ENV_FILE}" 74 | else 75 | printf "%s 不存在,请检查,若配置过环境变量的可以忽略\n" "${ENV_FILE}" && return 1 76 | fi 77 | 78 | # 是否显示上下文 默认是 79 | if [ "${DISPLAY_CONTEXT}" -eq 0 ]; then 80 | IS_DISPLAY_CONTEXT=0 81 | fi 82 | } 83 | 84 | # 获取 config/script 目录路径 85 | # shellcheck disable=SC2034 86 | get_some_path() { 87 | check_env 88 | if [ "${panel}" = "elecv2p" ]; then 89 | SCR_PATH="/usr/local/app/script/Shell" 90 | CONF_PATH="/usr/local/app/script/Lists" 91 | elif [ "${panel}" = "qinglong_new" ]; then 92 | SCR_PATH="/ql/data/scripts" 93 | CONF_PATH="/ql/data/config" 94 | REPO_PATH="/ql/data/repo" 95 | elif [ "${panel}" = "qinglong" ]; then 96 | SCR_PATH="/ql/scripts" 97 | CONF_PATH="/ql/config" 98 | REPO_PATH="/ql/repo" 99 | else 100 | SCR_PATH="." 101 | CONF_PATH="." 102 | fi 103 | } 104 | 105 | # 检查账户权限 106 | check_root() { 107 | if [ "$(id -u)" -eq 0 ]; then 108 | printf "当前用户是 ROOT 用户,可以继续操作\n" && sleep 1 109 | else 110 | printf "当前非 ROOT 账号(或没有 ROOT 权限),无法继续操作,请更换 ROOT 账号或使用 su 命令获取临时 ROOT 权限\n" && exit 1 111 | fi 112 | } 113 | 114 | # 检查 jq 依赖 115 | check_jq_installed_status() { 116 | if [ -z "$(command -v jq)" ]; then 117 | printf "jq 依赖没有安装,开始安装...\n" 118 | check_root 119 | if [ "${panel}" ]; then 120 | apk add --no-cache jq 121 | elif [ "${system}" = "centos" ]; then 122 | yum update && yum install jq -y 123 | elif [ "${system}" = "macos" ]; then 124 | brew install jq 125 | else 126 | apt-get update && apt-get install jq -y 127 | fi 128 | if [ -z "$(command -v jq)" ]; then 129 | printf "jq 依赖安装失败,请检查!\n" && exit 1 130 | else 131 | printf "jq 依赖安装成功!\n" 132 | fi 133 | fi 134 | } 135 | 136 | # 检查 Java 依赖 137 | check_java_installed_status() { 138 | if [ -z "$(command -v java)" ]; then 139 | printf "Java 依赖没有安装,开始安装...\n" 140 | check_root 141 | if [ "${panel}" ]; then 142 | apk add --no-cache openjdk8 143 | fi 144 | if [ -z "$(command -v java)" ]; then 145 | printf "Java 依赖安装失败,请检查!\n" && exit 1 146 | else 147 | printf "Java 依赖安装成功!\n" 148 | fi 149 | fi 150 | } 151 | -------------------------------------------------------------------------------- /utils_json52toml.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Slurp; 7 | use JSON5; 8 | use TOML::Dumper; 9 | 10 | sub json52toml { 11 | my $json5 = read_file(@_) or die "[Error] JSON5 文件读取错误,请检查路径是否完全和正确"; 12 | my $perl_scalar = decode_json5 $json5; 13 | my $toml = TOML::Dumper->new->dump($perl_scalar); 14 | $toml =~ s!\\/!/!g; 15 | 16 | ( my $new_file ) = @_; 17 | $new_file =~ s/\.json5?$/\.toml/; 18 | 19 | open( OUT, ">:encoding(UTF-8)", $new_file ); 20 | print OUT "$toml"; 21 | close(OUT); 22 | } 23 | 24 | if ( @ARGV == 0 ) { 25 | print "[Info.Auto] 未提供传入参数,将启动自动匹配转换\n"; 26 | my @PATH = ( "./", "/ql/data/config/", "/ql/config/", "/usr/local/app/script/Lists/" ); 27 | my @NAME = ( "check.json5", "check.json", "notify.json5", "notify.json" ); 28 | my @files = ( $ENV{CHECK_CONFIG}, $ENV{NOTIFY_CONFIG_PATH} ); 29 | foreach my $path (@PATH) { 30 | foreach my $name (@NAME) { 31 | push @files, "$path$name"; 32 | } 33 | } 34 | 35 | foreach my $file (@files) { 36 | if ( $file and -e $file ) { 37 | print "\t[Info] $file 存在\n"; 38 | my $new_file = $file; 39 | $new_file =~ s/\.json5?$/\.toml/; 40 | if ( -e $new_file ) { 41 | print "\t\t[Info.Quit] $new_file 已存在,放弃转换\n"; 42 | } 43 | else { 44 | print "\t\t[Info.GoOn] $new_file 不存在,开始转换\n"; 45 | json52toml($file); 46 | } 47 | } 48 | } 49 | } 50 | 51 | elsif ( @ARGV == 1 ) { json52toml $ARGV[0] } 52 | 53 | else { 54 | die 55 | "\tUsage: perl $0 <待转换的全路径文件名>\n\te.g. perl $0 /usr/local/app/script/Lists/check.json5\n\t[Error] 用法错误,已退出!!!"; 56 | } 57 | -------------------------------------------------------------------------------- /utils_models.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from peewee import CharField, DateField, Model, SmallIntegerField, SqliteDatabase 4 | 5 | from utils_env import get_file_path 6 | 7 | db = SqliteDatabase(get_file_path("rss.db")) 8 | 9 | 10 | class BaseModel(Model): 11 | class Meta: 12 | database = db 13 | 14 | 15 | class Rss(BaseModel): 16 | feed = CharField(unique=True) 17 | title = CharField(max_length=20) 18 | url = CharField(max_length=255) 19 | before = SmallIntegerField() 20 | 21 | 22 | class History(BaseModel): 23 | url = CharField(max_length=255) 24 | publish_at = DateField(default=datetime.datetime.now) 25 | 26 | 27 | def create_tables(): 28 | with db: 29 | db.create_tables([Rss, History]) 30 | -------------------------------------------------------------------------------- /utils_task.js: -------------------------------------------------------------------------------- 1 | // @grant sudo 2 | 3 | // 查看当前任务数 4 | console.log($task.status()); 5 | /* 6 | -------------------- 7 | { running: 13, total: 20, sub: 2 } 8 | -------------------- 9 | */ 10 | 11 | // 添加任务(具体任务格式参考 https://github.com/elecV2/elecV2P-dei/blob/master/docs/06-task.md) 12 | let res = $task.add( 13 | [ 14 | { 15 | name: '$task 添加的任务 1', 16 | type: 'cron', 17 | time: '12 15 18 * * *', 18 | job: { 19 | type: 'exec', 20 | target: 'pm2 ls', 21 | }, 22 | }, 23 | { 24 | name: '$task 添加的任务 2', 25 | type: 'cron', 26 | time: '12 15 18 * * *', 27 | job: { 28 | type: 'exec', 29 | target: 'pm2 ls', 30 | }, 31 | }, 32 | ], 33 | { type: 'replace' } 34 | ); 35 | console.log('$task 添加任务结果', res); 36 | /* 37 | -------------------- 38 | >> success 39 | $task 添加任务结果 { 40 | rescode: 0, 41 | message: 'TASK: $task 添加的任务 1 started\n TASK: $task 添加的任务 2 started' 42 | } 43 | >> failure 44 | $task 添加任务结果 { 45 | rescode: 0, 46 | message: 'some task parameters may be invalid(please check docs: https://github.com/elecV2/elecV2P-dei/blob/master/docs/06-task.md)\n' + 47 | 'some task parameters may be invalid(please check docs: https://github.com/elecV2/elecV2P-dei/blob/master/docs/06-task.md)' 48 | } 49 | -------------------- 50 | */ 51 | 52 | // 如果需要添加多个任务,可使用 array 数组的形式 53 | // 比如 $task.add([{}, {}], { type: 'replace' }) 54 | // 第二个参数 options 可省略。 55 | // 如设置 type, 表示同名任务的更新方式。有三个有效值: 56 | // - replace 替换原同名任务 57 | // - addition 新增同名任务 58 | // - skip 跳过添加同名任务 59 | 60 | // 获取任务名及对应 taskid 61 | let tnlist = $task.nameList(); 62 | console.log(tnlist); 63 | /* 64 | -------------------- 65 | { 66 | '清空日志': 'BPvOKSlv', 67 | '软更新升级': 'rswqs1uC', 68 | 'Python安装(Docker下)': 'Au7FLiaY', 69 | '重启 elecV2P': 'xVrbflqZ', 70 | '任务添加并执行': 'qM9b9JAN', 71 | 'Shell 指令远程任务': 'nbLrklPI', 72 | ... 73 | } 74 | -------------------- 75 | */ 76 | 77 | // 返回的是类似于 { '任务名': taskid } 的 object 78 | // 通过该 object,可使用任务名快速查找任务 id 79 | // 如果任务列表不经常变化的话建议使用 $store.put 或 $cache 保存 80 | 81 | // 开始任务 82 | console.log($task.start('BPvOKSlv')); 83 | /* 84 | -------------------- 85 | { 86 | rescode: 0, 87 | message: '清空日志 is running', 88 | taskinfo: { 89 | name: '清空日志', 90 | type: 'cron', 91 | time: '30 18 23 * * *', 92 | job: { 93 | type: 'runjs', 94 | target: 'https://raw.githubusercontent.com/elecV2/elecV2P/master/script/JSFile/deletelog.js' 95 | }, 96 | id: 'BPvOKSlv', 97 | running: true, 98 | group: 'Jyhniazi' 99 | } 100 | } 101 | -------------------- 102 | */ 103 | 104 | // 停止任务 105 | console.log($task.stop(tnlist['清空日志'])); // 通过任务名查找任务 id 106 | /* 107 | -------------------- 108 | { 109 | rescode: 0, 110 | message: '清空日志 stopped', 111 | taskinfo: { 112 | name: '清空日志', 113 | type: 'cron', 114 | time: '30 18 23 * * *', 115 | job: { 116 | type: 'runjs', 117 | target: 'https://raw.githubusercontent.com/elecV2/elecV2P/master/script/JSFile/deletelog.js' 118 | }, 119 | id: 'BPvOKSlv', 120 | running: false, 121 | group: 'Jyhniazi' 122 | } 123 | } 124 | -------------------- 125 | */ 126 | 127 | // 删除任务 128 | console.log($task.delete('BPvOKSlv')); 129 | /* 130 | -------------------- 131 | { rescode: 0, message: 'TASK cron 清空日志 deleted' } 132 | -------------------- 133 | */ 134 | 135 | // 使用数组的形式传入 taskid,可批量开始/暂停/删除 定时任务 136 | $task.start(['m8LWPxDc', 'ataskid', tnlist['$task 添加的任务'], 'jxwQOSJZ']); 137 | // $task.stop(['taskid1', 'taskid2', ...]) 138 | // $task.delete(['taskid1', 'taskid2', ...]) 139 | 140 | // 查看某个任务信息 141 | let taskinfo = $task.info('rswqs1uC'); // 查看所有任务信息 $task.info() 或者 $task.info('all') 142 | console.log(taskinfo); 143 | /* 144 | -------------------- 145 | >> single 146 | { 147 | name: '软更新升级', 148 | type: 'cron', 149 | time: '30 58 23 * * *', 150 | job: { 151 | type: 'runjs', 152 | target: 'https://raw.githubusercontent.com/elecV2/elecV2P/master/script/JSFile/softupdate.js' 153 | }, 154 | id: 'rswqs1uC', 155 | running: true, 156 | group: 'Jyhniazi' 157 | } 158 | -------------------- 159 | >> all 160 | { 161 | rswqs1uC: { 162 | name: '软更新升级', 163 | type: 'cron', 164 | time: '30 58 23 * * *', 165 | job: { 166 | type: 'runjs', 167 | target: 'https://raw.githubusercontent.com/elecV2/elecV2P/master/script/JSFile/softupdate.js' 168 | }, 169 | id: 'rswqs1uC', 170 | running: true, 171 | group: 'Jyhniazi' 172 | }, 173 | Au7FLiaY: { 174 | name: 'Python安装(Docker下)', 175 | type: 'schedule', 176 | time: '0', 177 | job: { 178 | type: 'runjs', 179 | target: 'https://raw.githubusercontent.com/elecV2/elecV2P/master/script/JSFile/python-install.js' 180 | }, 181 | running: false, 182 | id: 'Au7FLiaY', 183 | group: 'Jyhniazi' 184 | }, 185 | xVrbflqZ: { 186 | name: '重启 elecV2P', 187 | type: 'schedule', 188 | time: '0', 189 | job: { type: 'exec', target: 'pm2 restart elecV2P' }, 190 | running: false, 191 | id: 'xVrbflqZ', 192 | group: 'Jyhniazi' 193 | }, 194 | qM9b9JAN: { 195 | name: '任务添加并执行', 196 | type: 'schedule', 197 | time: '10', 198 | job: { type: 'exec', target: 'node -v' }, 199 | running: false, 200 | id: 'qM9b9JAN', 201 | group: 'Jyhniazi' 202 | }, 203 | nbLrklPI: { 204 | name: 'Shell 指令远程任务', 205 | type: 'schedule', 206 | time: '0', 207 | job: { 208 | type: 'exec', 209 | target: 'python3 https://raw.githubusercontent.com/elecV2/elecV2P/master/script/Shell/elecV2P-exam.py' 210 | }, 211 | running: false, 212 | id: 'nbLrklPI', 213 | group: 'Jyhniazi' 214 | }, 215 | WrOz0txU: { 216 | name: 'News', 217 | type: 'sub', 218 | job: { 219 | type: 'replace', 220 | target: 'https://raw.githubusercontent.com/Oreomeow/VIP/main/Tasks/News.json' 221 | }, 222 | update_type: 'none' 223 | }, 224 | FSEh4K9D: { 225 | name: 'Wool', 226 | type: 'group', 227 | note: '羊毛项目', 228 | bkcolor: '#3de1ad', 229 | collapse: true, 230 | total: 1, 231 | active: 0 232 | }, 233 | Jyhniazi: { 234 | name: 'elecV2P', 235 | type: 'group', 236 | note: '系统任务', 237 | bkcolor: '#f47983', 238 | collapse: true, 239 | total: 6, 240 | active: 2 241 | }, 242 | UiRsiwmO: { 243 | name: 'News', 244 | type: 'group', 245 | note: '新闻讯息', 246 | bkcolor: '#30dff3', 247 | collapse: true, 248 | total: 7, 249 | active: 5 250 | }, 251 | ... 252 | } 253 | -------------------- 254 | */ 255 | 256 | // 查询 __taskid/__taskname (仅在使用定时任务运行脚本时有值,其他情况默认为 undefined 257 | console.log('执行该脚本的任务名:', __taskname); 258 | console.log('相关任务信息', $task.info(__taskid)); 259 | 260 | // 尝试使用 __taskid 来停止自身定时任务 261 | if (__taskid) { 262 | let stopinfo = $task.stop(__taskid); // 停止自身定时任务 263 | console.log(stopinfo); 264 | } 265 | 266 | // 保存当前任务列表 267 | let saveres = $task.save(); 268 | console.log(saveres); 269 | /* 270 | -------------------- 271 | { rescode: 0, message: 'success save current task list 12/19/2' } 272 | -------------------- 273 | */ 274 | -------------------------------------------------------------------------------- /utils_tmp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | act_list = [ 3 | { 4 | "act_name": "赚积分", 5 | "aid": 1418, 6 | "if_task": True, # 是否有任务 7 | "referer": "https://hd.oppo.com/act/m/2021/jifenzhuanpan/index.html?us=gerenzhongxin&um=hudongleyuan&uc=yingjifen", 8 | "if_draw": False, # 是否有抽奖活动,不推荐开启抽奖,建议手动 9 | "extra_draw_cookie": 'app_innerutm={"uc":"yingjifen","um":"hudongleyuan","ut":"direct","us":"gerenzhongxin"};', 10 | # 抽奖必要的额外cookie信息,请勿随意修改,否则可能导致不中奖 11 | "lid": 1307, # 抽奖参数 12 | "draw_times": 3, # 控制抽奖次数3 13 | "end_time": "2033-8-18 23:59:59", # 长期任务 14 | "text": "每次扣取0积分,任务获取次数", 15 | }, 16 | { 17 | "act_name": "realme积分大乱斗-8月", 18 | "aid": 1582, 19 | "if_task": True, 20 | "referer": "https://hd.oppo.com/act/m/2021/2021/realmejifendalu/index.html", 21 | "if_draw": False, # 不推荐开启抽奖,建议手动 22 | "extra_draw_cookie": 'app_innerutm={"uc":"renwuzhongxin","um":"hudongleyuan","ut":"direct","us":"gerenzhongxin"};', 23 | "lid": 1466, 24 | "draw_times": 3, 25 | "end_time": "2022-8-31 23:59:59", 26 | "text": "每次扣取5积分,测试仍然可以中奖", 27 | }, 28 | { 29 | "act_name": "realme积分大乱斗-9月", 30 | "aid": 1582, 31 | "if_task": True, 32 | "referer": "https://hd.oppo.com/act/m/2021/2021/realmejifendalu/index.html?&us=realmenewshouye&um=yaofen&ut=right&uc=realmedaluandou", 33 | "if_draw": False, # 不推荐开启抽奖,建议手动 34 | "extra_draw_cookie": 'app_innerutm={"uc":"renwuzhongxin","um":"hudongleyuan","ut":"direct","us":"gerenzhongxin"};', 35 | "lid": 1554, # 抽奖接口与8月不一样,测试可以独立抽奖 36 | "draw_times": 3, 37 | "end_time": "2022-8-31 23:59:59", 38 | "text": "每次扣取5积分", 39 | }, 40 | { 41 | "act_name": "realme积分大乱斗-9月(2)", 42 | "aid": 1598, 43 | "if_task": True, 44 | "referer": "https://hd.oppo.com/act/m/2021/huantaishangchengjif/index.html?&us=realmeshouye&um=icon&ut=3&uc=realmejifendaluandou", 45 | "if_draw": False, # 不推荐开启抽奖,建议手动 46 | "extra_draw_cookie": 'app_innerutm={"uc":"realmejifendaluandou","um":"icon","ut":"3","us":"realmeshouye"};', 47 | "lid": 1535, 48 | "draw_times": 3, 49 | "end_time": "2022-8-31 23:59:59", 50 | "text": "每次扣取10积分", 51 | }, 52 | { 53 | "act_name": "天天积分翻倍", 54 | "aid": 675, 55 | "if_task": False, # 该活动没有任务 56 | "referer": "https://hd.oppo.com/act/m/2019/jifenfanbei/index.html?us=qiandao&um=task", 57 | "if_draw": False, # 不推荐开启抽奖,建议手动 58 | "extra_draw_cookie": 'app_innerutm={"uc":"direct","um":"zuoshangjiao","ut":"direct","us":"shouye"};', 59 | "lid": 1289, 60 | "draw_times": 1, 61 | "end_time": "2033-8-18 23:59:59", # 长期任务 62 | "text": "每次扣取10积分", 63 | }, 64 | { 65 | "act_name": "双十一活动", 66 | "aid": 1768, 67 | "if_task": True, 68 | "referer": "https://hd.oppo.com/act/m/2021/choumiandan/index.html?us=shouye&um=youshangjiao&uc=2021oppowin", 69 | "if_draw": False, # 不推荐开启抽奖,建议手动 70 | "extra_draw_cookie": 'app_innerutm={"uc":"direct","um":"direct","ut":"direct","us":"direct"};', 71 | "lid": 1586, 72 | "draw_times": 1, 73 | "end_time": "2021-11-13 23:59:59", 74 | "text": "每次扣取5积分", 75 | }, 76 | { 77 | "act_name": "OPPO会员日", 78 | "aid": 1825, 79 | "if_task": True, 80 | "referer": "https://hd.oppo.com/act/m/2021/3719/index.html?us=iotchannel&um=icon", 81 | "if_draw": False, # 不推荐开启抽奖,建议手动 82 | "extra_draw_cookie": 'app_innerutm={"uc":"direct","um":"direct","ut":"direct","us":"direct"};', 83 | "lid": 1606, 84 | "draw_times": 0, 85 | "end_time": "2021-11-16 23:59:59", 86 | "text": "每次扣取5积分", 87 | }, 88 | { 89 | "act_name": "一加加油站", 90 | "aid": 1473, 91 | "if_task": True, 92 | "referer": "https://hd.oppo.com/act/m/2021/OnePlusJYStation/index.html?us=onepluschannel&um=active", 93 | "if_draw": False, # 不推荐开启抽奖,建议手动 94 | "extra_draw_cookie": 'app_innerutm={"uc":"direct","um":"direct","ut":"direct","us":"direct"};', 95 | "lid": 1606, 96 | "draw_times": 0, 97 | "end_time": "2022-11-16 23:59:59", # 没写结束时间,先设置长期 98 | "text": "每次扣取0积分", 99 | }, 100 | ] 101 | 102 | budget_list = [ 103 | {"level": "Basic", "budget": 0}, 104 | {"vip": 0, "level": "Free", "budget": 10}, 105 | {"vip": 10, "level": "Free", "budget": 10}, 106 | {"vip": 11, "level": "Edu", "budget": 50}, 107 | {"vip": 21, "level": "Basic", "budget": 200}, 108 | {"vip": 31, "level": "Pro", "budget": 500}, 109 | {"vip": 41, "level": "Team", "budget": 2000}, 110 | {"vip": 51, "level": "Enterprise", "budget": 5000}, 111 | ] 112 | -------------------------------------------------------------------------------- /utils_ver.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | import time 5 | 6 | import requests 7 | 8 | __version__ = "20221108-4-010" 9 | ver_re = re.compile("__version__ = .(\\d+-\\d+-...).") 10 | 11 | 12 | def get_present_ver() -> str: 13 | return f"checkinpanel 当前版本:{__version__}" 14 | 15 | 16 | def get_latest_ver() -> str: 17 | url = "https://ghproxy.com/https://raw.githubusercontent.com/Oreomeow/checkinpanel/master/utils_ver.py" 18 | if time.localtime().tm_hour < 8 or time.localtime().tm_hour > 12: 19 | return "不在 8-12 点内,跳过版本检查。" 20 | try: 21 | r = requests.get(url=url, timeout=3) 22 | except Exception as e: 23 | ver_msg = f"获取最新版本失败,错误信息如下:\n{e}" 24 | else: 25 | latest_ver = ver_re.findall(r.text)[0] if ver_re.findall(r.text) else "无效版本" 26 | ver_msg = f"最新版本:{latest_ver}" 27 | return ver_msg 28 | 29 | 30 | def print_ver(): 31 | print(f"{get_present_ver()},{get_latest_ver()}\n") 32 | --------------------------------------------------------------------------------