├── .idea ├── vcs.xml ├── .gitignore ├── encodings.xml ├── git_toolbox_prj.xml ├── misc.xml ├── compiler.xml └── jarRepositories.xml ├── src └── main │ └── java │ └── cloud │ └── ohiyou │ ├── vo │ ├── CookieSignResult.java │ └── SignResultVO.java │ ├── utils │ ├── HiFiNiEncryptUtil.java │ ├── WeChatWorkUtils.java │ └── DingTalkUtils.java │ ├── ObsoleteCode.java │ └── Main.java ├── .github └── workflows │ └── HiFiNi-Auto-CheckIn.yml ├── READMES ├── WeChatWorkRobotConfigInfo.md └── DingTalkRobotConfigInfo.md ├── pom.xml ├── README.md └── .gitignore /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/git_toolbox_prj.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/cloud/ohiyou/vo/CookieSignResult.java: -------------------------------------------------------------------------------- 1 | package cloud.ohiyou.vo; 2 | 3 | public class CookieSignResult { 4 | private SignResultVO signResult; 5 | private long duration; 6 | 7 | public CookieSignResult(SignResultVO signResult, long duration) { 8 | this.signResult = signResult; 9 | this.duration = duration; 10 | } 11 | 12 | public SignResultVO getSignResult() { 13 | return signResult; 14 | } 15 | 16 | public void setSignResult(SignResultVO signResult) { 17 | this.signResult = signResult; 18 | } 19 | 20 | public long getDuration() { 21 | return duration; 22 | } 23 | 24 | public void setDuration(long duration) { 25 | this.duration = duration; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/HiFiNi-Auto-CheckIn.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Ant 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-ant 3 | 4 | name: HiFINI-Auto-CheckIn 5 | 6 | on: 7 | workflow_dispatch: 8 | schedule: 9 | - cron: "30 22 * * *" # 北京时间上午06:30 10 | 11 | jobs: 12 | build: 13 | env: 14 | COOKIE: ${{ secrets.COOKIE }} 15 | SERVER_CHAN: ${{ secrets.SERVER_CHAN }} 16 | DINGTALK_WEBHOOK: ${{ secrets.DINGTALK_WEBHOOK }} 17 | WXWORK_WEBHOOK: ${{ secrets.WXWORK_WEBHOOK }} 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Set up JDK 8 23 | uses: actions/setup-java@v3 24 | with: 25 | java-version: '8' 26 | distribution: 'temurin' 27 | - name: Build with Ant 28 | run: | 29 | mvn compile 30 | mvn exec:java -Dexec.mainClass="cloud.ohiyou.Main" 31 | -------------------------------------------------------------------------------- /src/main/java/cloud/ohiyou/vo/SignResultVO.java: -------------------------------------------------------------------------------- 1 | package cloud.ohiyou.vo; 2 | 3 | /** 4 | * @author ohiyou 5 | * @since 2024/2/21 11:37 6 | */ 7 | public class SignResultVO { 8 | private Integer code; 9 | private String message; 10 | private String userName; 11 | 12 | public SignResultVO(int code, String message) { 13 | this.code = code; 14 | this.message = message; 15 | } 16 | 17 | public Integer getCode() { 18 | return code; 19 | } 20 | 21 | public void setCode(Integer code) { 22 | this.code = code; 23 | } 24 | 25 | public String getMessage() { 26 | return message; 27 | } 28 | 29 | public void setMessage(String message) { 30 | this.message = message; 31 | } 32 | 33 | public String getUserName() { 34 | return userName; 35 | } 36 | 37 | public void setUserName(String userName) { 38 | this.userName = userName; 39 | } 40 | 41 | 42 | @Override 43 | public String toString() { 44 | return "SignResultVO{" + 45 | "code=" + code + 46 | ", message='" + message + '\'' + 47 | ", userName='" + userName + '\'' + 48 | '}'; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/cloud/ohiyou/utils/HiFiNiEncryptUtil.java: -------------------------------------------------------------------------------- 1 | package cloud.ohiyou.utils; 2 | 3 | /** 4 | * @author ohiyou 5 | * @since 2024/3/18 9:15 6 | */ 7 | public class HiFiNiEncryptUtil { 8 | /** 9 | * 待加密的字符串 10 | */ 11 | // public static final String SIGN = "729a5b06332b6a6a8b1f2841730af3343d9bd871be8dd631f83cbe9dcdc6b48b"; 12 | 13 | /** 14 | * 生成动态密钥的函数 15 | * @return String 16 | */ 17 | public static String generateDynamicKey() { 18 | // 获取当前时间的时间戳(毫秒) 19 | long currentTime = System.currentTimeMillis(); 20 | // 每5分钟变化一次,总共5个密钥轮换 21 | int keyIndex = (int) ((currentTime / (5 * 60 * 1000)) % 5); 22 | String[] keys = {"HIFINI", "HIFINI_COM", "HIFINI.COM", "HIFINI-COM", "HIFINICOM"}; 23 | return keys[keyIndex]; 24 | } 25 | 26 | /** 27 | * 简单的异或加密函数 28 | * @param input input 29 | * @param key key 30 | * @return String 31 | */ 32 | public static String simpleEncrypt(String input, String key) { 33 | StringBuilder result = new StringBuilder(); 34 | for (int i = 0; i < input.length(); i++) { 35 | char inputChar = input.charAt(i); 36 | char keyChar = key.charAt(i % key.length()); 37 | result.append((char) (inputChar ^ keyChar)); 38 | } 39 | return result.toString(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /READMES/WeChatWorkRobotConfigInfo.md: -------------------------------------------------------------------------------- 1 | # 配置企业微信机器人推送 2 | `以下方法使用PC端为例` 3 | 1. 登录企业微信,创建一个企业 **(必须创建企业而不是创建团队)** 4 | 5 | 2. 打开企业微信,消息页面点击➕,发起群聊,随便选两个人。(由于微信限制,必须选中额外的两个人,~~然后将他们踢出群聊就行,不然TA就能看到你的签到信息。~~💦) 6 | 。 7 | 8 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/ed95d53e-8d49-4138-8774-46d59ac4c55e) 9 | 10 | `群聊名称可以随便修改` 11 | 12 | 2. 进入群聊,点击右上角三个点 `···` --> 添加群机器人 --> 添加机器人 13 | 14 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/e7496390-8e97-4b40-b942-84d3a8bd5d5f) 15 | 16 | 在弹出的页面中点击`新创建一个机器人` 17 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/133d5140-199f-43b2-b2ce-354075efa83a) 18 | 19 | 给机器人随便取个名字后,点击添加机器人 20 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/c09c877e-7204-46c5-96ff-05ce1435dcb3) 21 | 22 | 出现以下页面代表设置完成 23 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/736c6679-2827-4f3f-86f0-38b84f21bd8f) 24 | 25 | **❗❗❗复制地址❗❗❗** 26 | ``` 27 | 复制之后你会得到如下: 28 | https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=12345678910xxxxxxxxxx 29 | 30 | 我们不需要等号前面的信息,只需要等号后面的信息,如:`12345678910xxxxxxxxxx`,然后将这段数据设置到对应的环境变量中去。 31 | ``` 32 | 33 | 如果不小心将该窗口关闭,可以点击`进入群-->在右侧找到机器人-->左健添加的机器人`就可找到信息了,在这里面还可以设置关键词、对机器人改名、删除机器人等操作。 34 | 35 | 成功案例: 36 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/9833c006-181a-4eb7-a59a-2b43ba58beab) 37 | 38 | 39 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | cloud.ohiyou 8 | HifiNiAutoCheckIn 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | UTF-8 15 | 16 | 17 | 18 | 19 | com.squareup.okhttp3 20 | okhttp 21 | 4.9.0 22 | 23 | 24 | 25 | com.alibaba 26 | fastjson 27 | 2.0.32 28 | 29 | 30 | 31 | 32 | com.aliyun 33 | alibaba-dingtalk-service-sdk 34 | 2.0.0 35 | 36 | 37 | 38 | 39 | commons-codec 40 | commons-codec 41 | 1.11 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 稀土掘金 3 |
4 | 5 |

HiFiNi - 音乐磁场签到助手

6 | 7 |

签到、推送、自动化工作流。

8 | 9 | ## 通知 10 | 11 | 🔈 支持设置多cookie 通过`&`分割,如:cookie1&cookie2(2024年3月29日17:57:46) 12 | 13 | 🔈 更新新的签到校验方式(2024年3月21日09:06:51) 14 | 15 | 🔈 对论坛新的签到校验方式进行适配(2024年3月18日19:37:13) 16 | 17 | 🔈 过renji验证 18 | 19 | 🔈 每天北京时间6.30执行签到任务(根据github当前时段的任务数量,可能会有延迟) 20 | 21 | ## 如何使用 22 | 23 | 1.[Fork 仓库](https://github.com/anduinnn/HiFiNi-Auto-CheckIn) 24 | 25 | 2.仓库 -> Settings -> Secrets -> New repository secret, 添加Secrets变量如下: 26 | 27 | | 变量名 | 信息 | 是否必须 | 28 | | ---------------- | ------------------------------------------- | -------- | 29 | | COOKIE | HiFiNi的cookie信息 | 是 | 30 | | SERVER_CHAN | [Service酱](https://sct.ftqq.com/)推送的key | 否 | 31 | | DINGTALK_WEBHOOK | 钉钉机器人推送的token | 否 | 32 | | WXWORK_WEBHOOK | 企业微信机器人推送的token | 否 | 33 | 34 | 3.启动工作流程 35 | ![image](https://github.com/anduinnn/HifiNiAutoCheckIn/assets/68073009/b89c7140-be7f-43aa-afaa-8554b4cab752) 36 | 37 | 38 | 39 | ## 如何拉取最新代码? 40 | 41 | 在自己的仓库里找到此项目 42 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/68073009/46ab90db-b7fb-4097-9abe-fde8c2c3543e) 43 | 44 | 45 | 46 | 47 | 48 | ## 获取HifiNiCookie 49 | 访问`https://www.hifini.com/` 50 | 首页`F12`打开调试工具,在请求标头中找到并复制cookie的值 51 | ![image](https://github.com/anduinnn/HifiNiAutoCheckIn/assets/68073009/97528823-4d31-4c72-bcca-e95bb5d75792) 52 | 53 | ## 获取Server酱的key(需要关注公众号) 54 | 访问 `https://sct.ftqq.com/` 55 | ![image](https://github.com/anduinnn/HifiNiAutoCheckIn/assets/68073009/c70b4471-2933-4441-964c-5aa2873c3590) 56 | 57 | ## 配置钉钉机器人 58 | 点击查看[如何配置钉钉机器人?](READMES/DingTalkRobotConfigInfo.md) 59 | 60 | ## 配置企业微信机器人 61 | 点击查看[如何配置企业微信机器人?](READMES/WeChatWorkRobotConfigInfo.md) 62 | -------------------------------------------------------------------------------- /READMES/DingTalkRobotConfigInfo.md: -------------------------------------------------------------------------------- 1 | # 配置钉钉机器人推送 2 | `以下方法使用PC端为例` 3 | 1. 创建群聊 4 | 5 | 打开钉钉,页面右上角➕,发起群聊。 6 | 7 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/e1e0e9d0-fc6c-4ebf-bdca-2ee6ecae2388) 8 | 9 | 选择普通群,随便选两个人。(由于钉钉限制,必须选中额外的两个人,~~然后将他们踢出群聊就行,不然TA就能看到你的签到信息。~~💦) 10 | 11 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/2a843acb-ef78-40ea-9248-3adfd025f509) 12 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/df7adb79-34c8-4d6b-969b-9c2edfea8ce0) 13 | 14 | `群聊名称可以随便修改` 15 | 16 | 2. 添加机器人 17 | 18 | 点击右上角群设置。 19 | 20 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/89ed565a-c26c-4ae9-954e-e2559c861540) 21 | 22 | 点击机器人 23 | 24 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/66c0fbbe-da38-404d-80a3-9982f792c036) 25 | 26 | 点击添加机器人x2 27 | 28 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/eae8f91f-6bf8-4571-803c-06d05978f326) 29 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/9d2aa7a9-3adc-4b7a-a2b2-6c8a940b41be) 30 | 31 | 选择自定义,点击添加 32 | 33 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/fbfca0bc-b981-415e-8362-7e37bf708db1) 34 | 35 | 填写机器人名字(随便)、**安全设置关键词(必须有`HiFiNi`或者`HiFiNi签到`,也可以两个都添加)** --> 勾选同意免责条款 --> 点击完成 36 | 37 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/c303beb5-beb0-4dbd-8c24-d8106b83e585) 38 | 39 | 出现以下页面代表设置完成 40 | 41 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/dcdda610-7045-4bc0-9e27-0f6f0f4c36bc) 42 | 43 | **❗❗❗复制Webhook❗❗❗** 44 | ``` 45 | 复制之后你会得到如下: 46 | https://oapi.dingtalk.com/robot/send?access_token=12345678910xxxxxxxxxx 47 | 48 | 我们不需要等号前面的信息,只需要等号后面的信息,如:`12345678910xxxxxxxxxx`,然后将这段数据设置到对应的环境变量中去。 49 | ``` 50 | 51 | 如果不小心将该窗口关闭,可以点击`群设置-->机器人-->选择刚刚添加的机器人`就可找到信息了,在这里面还可以设置关键词、对机器人改名、删除机器人等操作。 52 | 53 | 成功案例: 54 | 55 | ![image](https://github.com/anduinnn/HiFiNi-Auto-CheckIn/assets/115618748/5973ff0d-d8f9-4e1c-87b9-7e2c841793f5) 56 | 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Java template 2 | # 排除测试文件夹 3 | src/main/java/cloud/ohiyou/test 4 | 5 | # Compiled class file 6 | *.class 7 | 8 | # Log file 9 | *.log 10 | 11 | # BlueJ files 12 | *.ctxt 13 | 14 | # Mobile Tools for Java (J2ME) 15 | .mtj.tmp/ 16 | 17 | # Package Files # 18 | *.jar 19 | *.war 20 | *.nar 21 | *.ear 22 | *.zip 23 | *.tar.gz 24 | *.rar 25 | 26 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 27 | hs_err_pid* 28 | replay_pid* 29 | 30 | ### JetBrains template 31 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 32 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 33 | 34 | # User-specific stuff 35 | .idea/**/workspace.xml 36 | .idea/**/tasks.xml 37 | .idea/**/usage.statistics.xml 38 | .idea/**/dictionaries 39 | .idea/**/shelf 40 | 41 | # AWS User-specific 42 | .idea/**/aws.xml 43 | 44 | # Generated files 45 | .idea/**/contentModel.xml 46 | 47 | # Sensitive or high-churn files 48 | .idea/**/dataSources/ 49 | .idea/**/dataSources.ids 50 | .idea/**/dataSources.local.xml 51 | .idea/**/sqlDataSources.xml 52 | .idea/**/dynamic.xml 53 | .idea/**/uiDesigner.xml 54 | .idea/**/dbnavigator.xml 55 | 56 | # Gradle 57 | .idea/**/gradle.xml 58 | .idea/**/libraries 59 | 60 | # Gradle and Maven with auto-import 61 | # When using Gradle or Maven with auto-import, you should exclude module files, 62 | # since they will be recreated, and may cause churn. Uncomment if using 63 | # auto-import. 64 | # .idea/artifacts 65 | # .idea/compiler.xml 66 | # .idea/jarRepositories.xml 67 | # .idea/modules.xml 68 | # .idea/*.iml 69 | # .idea/modules 70 | # *.iml 71 | # *.ipr 72 | 73 | # CMake 74 | cmake-build-*/ 75 | 76 | # Mongo Explorer plugin 77 | .idea/**/mongoSettings.xml 78 | 79 | # File-based project format 80 | *.iws 81 | 82 | # IntelliJ 83 | out/ 84 | 85 | # mpeltonen/sbt-idea plugin 86 | .idea_modules/ 87 | 88 | # JIRA plugin 89 | atlassian-ide-plugin.xml 90 | 91 | # Cursive Clojure plugin 92 | .idea/replstate.xml 93 | 94 | # SonarLint plugin 95 | .idea/sonarlint/ 96 | 97 | # Crashlytics plugin (for Android Studio and IntelliJ) 98 | com_crashlytics_export_strings.xml 99 | crashlytics.properties 100 | crashlytics-build.properties 101 | fabric.properties 102 | 103 | # Editor-based Rest Client 104 | .idea/httpRequests 105 | 106 | # Android studio 3.1+ serialized cache file 107 | .idea/caches/build_file_checksums.ser 108 | 109 | -------------------------------------------------------------------------------- /src/main/java/cloud/ohiyou/utils/WeChatWorkUtils.java: -------------------------------------------------------------------------------- 1 | package cloud.ohiyou.utils; 2 | 3 | 4 | import com.alibaba.fastjson.JSON; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.dingtalk.api.request.OapiRobotSendRequest; 7 | import okhttp3.*; 8 | 9 | import java.io.IOException; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * 企业微信工具箱 14 | * @Author 银垚@mtouyao 15 | * @Date&Time 2024/3/7 16:40 16 | */ 17 | public class WeChatWorkUtils { 18 | 19 | private static final OkHttpClient client = new OkHttpClient.Builder() 20 | .connectTimeout(30, TimeUnit.SECONDS) 21 | .readTimeout(30, TimeUnit.SECONDS) 22 | .writeTimeout(30, TimeUnit.SECONDS) 23 | .build(); 24 | /** 25 | * 企业微信工具箱 26 | * @Author 银垚@mtouyao 27 | * @Date&Time 2024/3/7 16:40 28 | * @param WXWorkRobotKey 微信的机器人应用的 key 的值 29 | * @param messageText 签到信息 30 | * @param msgType (必选其一)text/markdown 31 | */ 32 | public static void pushBotMessage(String WXWorkRobotKey, String messageTitle, String messageText, String msgType){ 33 | 34 | if (WXWorkRobotKey == null || "".equals(WXWorkRobotKey)) { 35 | System.out.println("WXWORK_WEBHOOK 环境变量未设置"); 36 | return; 37 | } 38 | if (msgType == null) {msgType="markdown";} 39 | // 构建参数 40 | String jsonBody = ""; 41 | switch (msgType) { 42 | // 企业微信发送类型详解官网: https://developer.work.weixin.qq.com/document/path/91770 43 | // 发送text消息 44 | case "text": 45 | jsonBody = "{\"msgtype\": \"text\",\"text\": {\"content\":\"HiFiNi签到消息通知:"+messageTitle+messageText+"\"}}"; 46 | //定义文本内容 47 | break; 48 | //发送markdown消息 49 | case "markdown": 50 | jsonBody = "{\"msgtype\": \"markdown\",\"markdown\": {\"content\":\"# HiFiNi签到消息通知 \n ## "+messageTitle+" \n"+messageText+"\"}}"; 51 | // 定义markdown内容 52 | break; 53 | 54 | // 发送image消息(图片) 55 | case "image": 56 | System.out.println("[企业微信机器人]暂未开通image类型消息!"); 57 | break; 58 | // 发送news消息(图文) 59 | case "news": 60 | System.out.println("[企业微信机器人]暂未开通news类型消息!"); 61 | break; 62 | // 发送file消息(文件) 63 | case "file": 64 | System.out.println("[企业微信机器人]暂未开通file类型消息!"); 65 | break; 66 | // 发送voice消息(语音) 67 | case "voice": 68 | System.out.println("[企业微信机器人]暂未开通voice类型消息!"); 69 | break; 70 | 71 | /** 未匹配 */ 72 | default: 73 | System.out.println("[企业微信机器人]消息类型未匹配!请检查调用pushBotMessage传递的msgType参数"); 74 | break; 75 | 76 | } 77 | 78 | // 设置请求body 79 | RequestBody body = RequestBody.create(jsonBody, MediaType.get("application/json; charset=utf-8")); 80 | 81 | // 设置请求信息 82 | Request request = new Request.Builder() 83 | .url("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key="+WXWorkRobotKey) 84 | .post(body) 85 | .build(); 86 | 87 | // 发送请求 88 | try (Response response = client.newCall(request).execute()) { 89 | System.out.println("企业微信机器人返回信息:"+response.body().string()); 90 | } catch (IOException e) { 91 | e.printStackTrace(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/cloud/ohiyou/utils/DingTalkUtils.java: -------------------------------------------------------------------------------- 1 | package cloud.ohiyou.utils; 2 | import com.dingtalk.api.DefaultDingTalkClient; 3 | import com.dingtalk.api.DingTalkClient; 4 | import com.dingtalk.api.request.OapiRobotSendRequest; 5 | import com.dingtalk.api.response.OapiRobotSendResponse; 6 | import com.taobao.api.ApiException; 7 | import org.apache.commons.codec.binary.Base64; 8 | 9 | import javax.crypto.Mac; 10 | import javax.crypto.spec.SecretKeySpec; 11 | import java.io.UnsupportedEncodingException; 12 | import java.net.URLEncoder; 13 | import java.security.InvalidKeyException; 14 | import java.security.NoSuchAlgorithmException; 15 | import java.util.Arrays; 16 | 17 | /** 18 | * 钉钉工具箱 19 | * @Author 银垚@mtouyao 20 | * @Date&Time 2024/3/7 10:25 21 | */ 22 | public class DingTalkUtils { 23 | 24 | /** 25 | * 钉钉机器人 26 | * @Author 银垚@mtouyao 27 | * @Date&Time 2024/3/7 10:25 28 | * @param customRobotToken 你的机器人应用的 access_token 的值/your custom robot token 29 | * @param messageText 签到信息 30 | * @param userIDs (可选)用户的 userId 信息[仅限钉钉内部群使用]/you need @ group user's userId 31 | * @param msgType (必选其一)text/markdown 32 | */ 33 | public static void pushBotMessage(String customRobotToken, String messageTitle, String messageText, String userIDs, String msgType){ 34 | try { 35 | if (customRobotToken == null || "".equals(customRobotToken)) { 36 | System.out.println("DINGTALK_WEBHOOK 环境变量未设置"); 37 | return; 38 | } 39 | if (msgType == null) {msgType="markdown";} 40 | Long timestamp = System.currentTimeMillis(); 41 | // System.out.println(timestamp); // 时间戳 42 | String secret = "messageKey"; // 安全设置,加签密钥。 43 | String stringToSign = timestamp + "\n" + secret; 44 | Mac mac = Mac.getInstance("HmacSHA256"); 45 | mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")); 46 | byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8")); 47 | String sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)),"UTF-8"); 48 | // System.out.println(sign); // 签名 49 | 50 | //sign字段和timestamp字段必须拼接到请求URL上,否则会出现 310000 的错误信息 51 | DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?sign="+sign+"×tamp="+timestamp); 52 | OapiRobotSendRequest req = new OapiRobotSendRequest(); 53 | 54 | switch (msgType) { 55 | // 钉钉发送类型详解官网: https://open.dingtalk.com/document/orgapp/custom-robot-access 56 | // 发送text消息 57 | case "text": 58 | //定义文本内容 59 | OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text(); 60 | text.setContent("HiFiNi签到信息:"+messageTitle+messageText); 61 | //设置消息类型 62 | req.setMsgtype("text"); 63 | req.setText(text); 64 | break; 65 | //发送markdown消息 66 | case "markdown": 67 | // 定义markdown内容 68 | OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown(); 69 | markdown.setTitle("HiFiNiBot消息通知"); 70 | markdown.setText("# HiFiNi签到消息通知 \n ## "+messageTitle+" \n"+messageText); 71 | req.setMsgtype("markdown"); 72 | req.setMarkdown(markdown); 73 | break; 74 | 75 | // 发送link消息 76 | case "link": 77 | System.out.println("[钉钉机器人]暂未开通link类型消息!"); 78 | break; 79 | // 发送ActionCard消息 80 | case "ActionCard": 81 | System.out.println("[钉钉机器人]暂未开通ActionCard类型消息!"); 82 | break; 83 | // 发送FeedCard消息 84 | case "FeedCard": 85 | System.out.println("[钉钉机器人]暂未开通FeedCard类型消息!"); 86 | break; 87 | 88 | /** 未匹配 */ 89 | default: 90 | System.out.println("[钉钉机器人]消息类型未匹配!请检查调用pushBotMessage传递的msgType参数"); 91 | break; 92 | 93 | } 94 | 95 | //定义 @ 对象 96 | OapiRobotSendRequest.At at = new OapiRobotSendRequest.At(); 97 | at.setAtUserIds(Arrays.asList(userIDs)); 98 | req.setAt(at); 99 | OapiRobotSendResponse rsp = client.execute(req, customRobotToken); 100 | System.out.println("钉钉机器人返回信息:"+rsp.getBody()); 101 | } catch (ApiException e) { 102 | e.printStackTrace(); 103 | } catch (UnsupportedEncodingException e) { 104 | throw new RuntimeException(e); 105 | } catch (NoSuchAlgorithmException e) { 106 | throw new RuntimeException(e); 107 | } catch (InvalidKeyException e) { 108 | throw new RuntimeException(e); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/cloud/ohiyou/ObsoleteCode.java: -------------------------------------------------------------------------------- 1 | //package cloud.ohiyou; 2 | // 3 | //import cloud.ohiyou.vo.SignResultVO; 4 | //import okhttp3.MediaType; 5 | //import okhttp3.Request; 6 | //import okhttp3.RequestBody; 7 | //import okhttp3.Response; 8 | // 9 | //import java.io.IOException; 10 | //import java.util.regex.Matcher; 11 | //import java.util.regex.Pattern; 12 | // 13 | ///** 14 | // * 废弃的代码,先保留,以后可能会用到 15 | // */ 16 | //public class ObsoleteCode { 17 | // private static final String COOKIE = ""; 18 | // 19 | // 20 | // /** 21 | // * 获取签到的signKey 22 | // * 23 | // * @param result 24 | // * @return 25 | // * @throws IOException 26 | // */ 27 | // private static String getSignKey(String result) throws IOException { 28 | // String baseUrl = "https://www.hifini.com"; 29 | // // 获取 src 后的地址 30 | // Pattern patternSrc = Pattern.compile("src=\"([^\"]+)\""); 31 | // Matcher matcherSrc = patternSrc.matcher(result); 32 | // if (matcherSrc.find()) { 33 | // baseUrl += matcherSrc.group(1); 34 | // 35 | // // 发送renji请求 36 | // Request request = new Request.Builder() 37 | // .url(baseUrl) 38 | // .addHeader("Cookie", COOKIE) 39 | // .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)") 40 | // .build(); 41 | // 42 | // try (Response response = client.newCall(request).execute()) { 43 | // if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 44 | // 45 | // // 定义正则表达式模式 46 | // Pattern pattern = Pattern.compile("/renji_[a-f0-9]+_([a-f0-9]+)\\.js"); 47 | // Matcher matcher = pattern.matcher(result); 48 | // if (matcher.find()) { 49 | // return matcher.group(1); 50 | // } else { 51 | // throw new RuntimeException("未能通过人机校验"); 52 | // } 53 | // } 54 | // } else { 55 | // throw new RuntimeException("未能通过人机校验"); 56 | // } 57 | // } 58 | // 59 | // /** 60 | // * 发送签到请求 61 | // * 62 | // * @param cookieValue 63 | // * @param attempt 64 | // * @param maxAttempts 65 | // * @return 66 | // * @throws IOException 67 | // * @throws InterruptedException 68 | // */ 69 | // private static SignResultVO sendSignInRequest(String cookieValue, int attempt, int maxAttempts) throws IOException, InterruptedException { 70 | // if (attempt > maxAttempts) { 71 | // System.out.println("已达到最大尝试次数。正在停止执行。"); 72 | // return null; 73 | // } 74 | // 75 | // System.out.println("尝试第" + attempt + "次;最大:" + maxAttempts + "次"); 76 | // 77 | // Request request = new Request.Builder() 78 | // .url("https://www.hifini.com/sg_sign.htm") 79 | // .post(RequestBody.create("", MediaType.get("application/json; charset=utf-8"))) 80 | // .addHeader("Cookie", cookieValue) 81 | // .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36") 82 | // .addHeader("X-Requested-With", "XMLHttpRequest") 83 | // .build(); 84 | // 85 | // try (Response response = client.newCall(request).execute()) { 86 | // if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 87 | // 88 | // String result = readResponse(response); 89 | // if (result.contains("正在进行人机识别")) { 90 | // System.out.println("遇到CAPTCHA,正在尝试绕过。"); 91 | // 92 | // String key = getSignKey(result); 93 | // String token = getRenjiToken(key); 94 | // cookieValue = formatCookie(cookieValue) + " " + token; 95 | // 96 | // Thread.sleep(2000); 97 | // return sendSignInRequest(cookieValue, attempt + 1, maxAttempts); 98 | // } 99 | // 100 | // return stringToObject(result, SignResultVO.class); 101 | // } 102 | // } 103 | // 104 | // 105 | // /** 106 | // * 发送签到请求,最大五次尝试 107 | // * 108 | // * @param cookieValue cookieValue 109 | // * @return 110 | // * @throws IOException 111 | // * @throws InterruptedException 112 | // */ 113 | // private static SignResultVO initialSendSignInRequest(String cookieValue) throws IOException, InterruptedException { 114 | // return sendSignInRequest(cookieValue, 1, 5); 115 | // } 116 | // 117 | // private static String getRenjiToken(String key) throws IOException { 118 | // // MD5加密的字符串:renji 119 | // String baseUrl = "https://www.hifini.com/a20be899_96a6_40b2_88ba_32f1f75f1552_yanzheng_ip.php"; 120 | // String type = "96c4e20a0e951f471d32dae103e83881"; 121 | // String value = "05bb5aba7f3f54a677997f862f1a9020"; 122 | // 123 | // // 构建最终的 124 | // String urlWithParams = String.format("%s?type=%s&key=%s&value=%s", baseUrl, type, key, value); 125 | // 126 | // Request request = new Request.Builder() 127 | // .url(urlWithParams) 128 | // .addHeader("Cookie", COOKIE) 129 | // .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36") 130 | // .build(); 131 | // 132 | // try (Response response = client.newCall(request).execute()) { 133 | // if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 134 | // String header = response.header("Set-Cookie"); 135 | // if (header != null) { 136 | // String[] split = header.split(";"); 137 | // return split[0]; 138 | // } else { 139 | // throw new RuntimeException("未能通过人机校验"); 140 | // } 141 | // } 142 | // } 143 | // 144 | //} 145 | -------------------------------------------------------------------------------- /src/main/java/cloud/ohiyou/Main.java: -------------------------------------------------------------------------------- 1 | package cloud.ohiyou; 2 | 3 | /** 4 | * @author ohiyou 5 | * @since 2024/3/1 14:19 6 | */ 7 | 8 | import cloud.ohiyou.utils.DingTalkUtils; 9 | import cloud.ohiyou.utils.WeChatWorkUtils; 10 | import cloud.ohiyou.vo.CookieSignResult; 11 | import cloud.ohiyou.vo.SignResultVO; 12 | import com.alibaba.fastjson.JSON; 13 | import okhttp3.*; 14 | 15 | import java.io.BufferedReader; 16 | import java.io.IOException; 17 | import java.io.InputStreamReader; 18 | import java.net.URLEncoder; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.concurrent.*; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | public class Main { 26 | private static final String COOKIE = System.getenv("COOKIE"); 27 | // private static final String COOKIE = cloud.ohiyou.test.TestEnum.COOKIE.getValue(); 28 | private static final String DINGTALK_WEBHOOK = System.getenv("DINGTALK_WEBHOOK"); // 钉钉机器人 access_token 的值 29 | private static final String WXWORK_WEBHOOK = System.getenv("WXWORK_WEBHOOK"); // 企业微信机器人 key 的值 30 | private static final String SERVER_CHAN_KEY = System.getenv("SERVER_CHAN"); 31 | private static final OkHttpClient client = new OkHttpClient.Builder() 32 | .connectTimeout(30, TimeUnit.SECONDS) 33 | .readTimeout(30, TimeUnit.SECONDS) 34 | .writeTimeout(30, TimeUnit.SECONDS) 35 | .build(); 36 | 37 | public static void main(String[] args) { 38 | List results = new ArrayList<>(); 39 | if (COOKIE == null) { 40 | throw new RuntimeException("未设置Cookie"); 41 | } 42 | 43 | String[] cookiesArray = COOKIE.split("&"); 44 | log("检测到 " + cookiesArray.length + " 个cookie"); 45 | 46 | ExecutorService executor = Executors.newFixedThreadPool(5); 47 | List> futures = new ArrayList<>(); 48 | for (int i = 0; i < cookiesArray.length; i++) { 49 | final String cookie = cookiesArray[i]; 50 | final int index = i; 51 | Future future = executor.submit(() -> { 52 | try { 53 | processCookie(cookie, index, results); 54 | } catch (Exception e) { 55 | log("Error processing cookie at index " + index + ": " + e.getMessage()); 56 | } 57 | }); 58 | futures.add(future); 59 | } 60 | 61 | // 关闭线程池,使其不再接受新任务 62 | executor.shutdown(); 63 | 64 | // 等待所有任务完成 65 | for (Future future : futures) { 66 | try { 67 | // 等待并获取任务执行结果,这会阻塞,直到任务完成 68 | future.get(); 69 | } catch (InterruptedException e) { 70 | Thread.currentThread().interrupt(); // 重新中断当前线程 71 | // 处理中断异常,例如记录日志或者根据业务需求进行其他处理 72 | log("当前线程在等待任务完成时被中断。"); 73 | } catch (ExecutionException e) { 74 | // 获取实际导致任务执行失败的异常 75 | Throwable cause = e.getCause(); 76 | log("执行任务时出错:" + cause.getMessage()); 77 | } 78 | } 79 | 80 | // 等待线程池完全终止 81 | try { 82 | if (!executor.awaitTermination(20, TimeUnit.SECONDS)) { 83 | executor.shutdownNow(); 84 | } 85 | } catch (InterruptedException e) { 86 | executor.shutdownNow(); 87 | } 88 | 89 | client.dispatcher().executorService().shutdownNow(); 90 | client.connectionPool().evictAll(); 91 | 92 | publishResults(results); 93 | } 94 | 95 | private static void processCookie(String cookie, int index, List results) { 96 | long startTime = System.currentTimeMillis(); 97 | String formattedCookie = formatCookie(cookie.trim(), index); 98 | if (formattedCookie != null) { 99 | SignResultVO signResultVO = sendSignInRequest(formattedCookie); 100 | long endTime = System.currentTimeMillis(); 101 | results.add(new CookieSignResult(signResultVO, endTime - startTime)); 102 | } 103 | } 104 | 105 | private static void publishResults(List results) { 106 | StringBuilder messageBuilder = new StringBuilder(); 107 | boolean allSuccess = true; // 假设所有签到都成功,直到发现失败的签到 108 | 109 | for (CookieSignResult result : results) { 110 | messageBuilder.append(result.getSignResult().getUserName()).append(": ") 111 | .append("\n签到结果: ").append(result.getSignResult().getMessage()) 112 | .append("\n耗时: ").append(result.getDuration()).append("ms\n\n"); 113 | // 检查每个签到结果,如果有失败的,则设置allSuccess为false 114 | if (!result.getSignResult().getMessage().contains("成功签到")) { 115 | allSuccess = false; 116 | } 117 | } 118 | 119 | String title = allSuccess ? "签到成功" : "签到失败"; // 根据所有签到结果决定标题 120 | // 推送 121 | publishWechat(SERVER_CHAN_KEY, title,messageBuilder.toString()); 122 | DingTalkUtils.pushBotMessage(DINGTALK_WEBHOOK, title, messageBuilder.toString(), "", "markdown"); 123 | WeChatWorkUtils.pushBotMessage(WXWORK_WEBHOOK, title, messageBuilder.toString(), "markdown"); 124 | } 125 | 126 | 127 | 128 | private static SignResultVO sendSignInRequest(String cookie) { 129 | // 获取签到页面 130 | String signPageCode = getSignPage(cookie); 131 | String sign = getSign(signPageCode); 132 | String userName = getUserName(signPageCode); 133 | // 获取加密参数 134 | // String dynamicKey = HiFiNiEncryptUtil.generateDynamicKey(); 135 | // String encryptedSign = HiFiNiEncryptUtil.simpleEncrypt(sign, dynamicKey); 136 | 137 | RequestBody formBody = new FormBody.Builder() 138 | .add("sign", sign) 139 | .build(); 140 | // 发送签到请求 141 | Request request = new Request.Builder() 142 | .url("https://www.hifini.com/sg_sign.htm") 143 | .post(formBody) 144 | .addHeader("Cookie", cookie) 145 | .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36") 146 | .addHeader("X-Requested-With", "XMLHttpRequest") 147 | .build(); 148 | try (Response response = client.newCall(request).execute()) { 149 | if (!response.isSuccessful()) { 150 | throw new IOException("Unexpected code " + response); 151 | } 152 | ; 153 | String result = readResponse(response); 154 | SignResultVO signResultVO = stringToObject(result, SignResultVO.class); 155 | signResultVO.setUserName(userName); 156 | return signResultVO; 157 | } catch (IOException e) { 158 | e.printStackTrace(); 159 | } 160 | System.out.println(); 161 | return null; 162 | } 163 | 164 | private static String getUserName(String signPageCode) { 165 | String userName = "未知"; 166 | 167 | // 使用正则匹配用户名 168 | Pattern pattern = Pattern.compile("
  • (.*?)
  • "); 169 | Matcher matcher = pattern.matcher(signPageCode); 170 | 171 | if (matcher.find()) { 172 | userName = matcher.group(1); 173 | } 174 | return userName; 175 | } 176 | 177 | private static String getSign(String signPageCode) { 178 | // 通过正则获取sign的值 179 | if (signPageCode.contains("请登录")) { 180 | throw new RuntimeException("cookie失效"); 181 | } 182 | 183 | // 使用正则表达式匹配sign变量的值 184 | Pattern pattern = Pattern.compile("var sign = \"([^\"]+)\""); 185 | Matcher matcher = pattern.matcher(signPageCode); 186 | 187 | if (matcher.find()) { 188 | // 如果找到了匹配,提取第一组(括号内的部分) 189 | String signValue = matcher.group(1); 190 | System.out.println("Sign的值是: " + signValue); 191 | return signValue; 192 | } else { 193 | throw new RuntimeException("未能获取sign,请检查cookie是否失效"); 194 | } 195 | } 196 | 197 | /** 198 | * 获取加密的key 199 | * 200 | * @param cookie cookie 201 | * @return String 202 | */ 203 | private static String getSignPage(String cookie) { 204 | // 先携带cookie访问一次签到页面获取sign 205 | Request request = new Request.Builder() 206 | .url("https://www.hifini.com/sg_sign.htm") 207 | .get() 208 | .addHeader("Cookie", cookie) 209 | .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/") 210 | .build(); 211 | try (Response response = client.newCall(request).execute()) { 212 | if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); 213 | return readResponse(response); 214 | } catch (IOException e) { 215 | e.printStackTrace(); 216 | } 217 | throw new RuntimeException("未能获取sign,请检查cookie是否失效"); 218 | } 219 | 220 | private static String formatCookie(String cookie, int index) { 221 | String bbsSid = null; 222 | String bbsToken = null; 223 | 224 | // 分割cookie字符串 225 | String[] split = cookie.split(";"); 226 | 227 | // 遍历分割后的字符串数组 228 | for (String s : split) { 229 | s = s.trim(); // 去除可能的前后空格 230 | // 检查当前字符串是否包含bbs_sid或bbs_token 231 | if (s.startsWith("bbs_sid=")) { 232 | bbsSid = s; // 存储bbs_sid 233 | } else if (s.startsWith("bbs_token=")) { 234 | bbsToken = s; // 存储bbs_token 235 | } 236 | } 237 | 238 | // 确保bbs_sid和bbs_token都不为空 239 | if (bbsSid != null && bbsToken != null) { 240 | log("成功解析第 " + (index + 1) + " 个cookie"); 241 | // 拼接bbs_sid和bbs_token并返回 242 | return bbsSid + ";" + bbsToken + ";"; 243 | } else { 244 | log("解析第 " + (index + 1) + " 个cookie失败"); 245 | return null; // 或者根据需要抛出异常 246 | } 247 | } 248 | 249 | 250 | 251 | private static void log(String message) { 252 | System.out.println(message); 253 | } 254 | 255 | private static String readResponse(Response response) throws IOException { 256 | BufferedReader reader = new BufferedReader(new InputStreamReader(response.body().byteStream())); 257 | StringBuilder result = new StringBuilder(); 258 | String readLine; 259 | while ((readLine = reader.readLine()) != null) { 260 | result.append(readLine); 261 | } 262 | return result.toString(); 263 | } 264 | 265 | 266 | private static T stringToObject(String result, Class clazz) { 267 | return JSON.parseObject(result, clazz); 268 | } 269 | 270 | private static void publishWechat(String serverChanKey, String title, String body) { 271 | if (serverChanKey == null) { 272 | log("SERVER_CHAN 环境变量未设置"); 273 | return; 274 | } 275 | 276 | try { 277 | String url = "https://sctapi.ftqq.com/" + serverChanKey + ".send?title=" + 278 | URLEncoder.encode(title, "UTF-8") + "&desp=" + URLEncoder.encode(body, "UTF-8"); 279 | Request request = new Request.Builder() 280 | .url(url) 281 | .build(); 282 | client.newCall(request).execute(); 283 | } catch (Exception e) { 284 | e.printStackTrace(); 285 | } 286 | } 287 | 288 | } 289 | --------------------------------------------------------------------------------