├── .github ├── trigger.json ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── auto_package_bilibili-helper.yml ├── docs └── IMG │ ├── a.jpg │ ├── b.jpg │ ├── c.jpg │ ├── d.jpg │ ├── a0.jpg │ ├── qqgroup.png │ ├── openActions.png │ ├── serverpush.png │ ├── 20201012001307.png │ ├── 20201013134942.png │ ├── 20201013210000.png │ ├── wechatMsgPush.png │ ├── workflow_dispatch.png │ └── jetbrains.svg ├── src ├── main │ ├── resources │ │ ├── release.json │ │ ├── release.info │ │ ├── config.json │ │ ├── log4j2.xml │ │ └── logging.properties │ └── java │ │ ├── top │ │ └── misec │ │ │ ├── pojo │ │ │ ├── rewardbean │ │ │ │ ├── JsonRootBean.java │ │ │ │ └── RewardData.java │ │ │ └── userinfobean │ │ │ │ ├── Vip_label.java │ │ │ │ ├── JsonRootBean.java │ │ │ │ ├── Official.java │ │ │ │ ├── OfficialVerify.java │ │ │ │ ├── Wallet.java │ │ │ │ ├── Pendant.java │ │ │ │ ├── Level_info.java │ │ │ │ └── Data.java │ │ │ ├── task │ │ │ ├── Task.java │ │ │ ├── ExtensionMethod.java │ │ │ ├── MangaSign.java │ │ │ ├── LiveCheckin.java │ │ │ ├── MangaRead.java │ │ │ ├── UserCheck.java │ │ │ ├── Silver2coin.java │ │ │ ├── TaskInfoHolder.java │ │ │ ├── ServerPush.java │ │ │ ├── VideoWatch.java │ │ │ ├── GetVipPrivilege.java │ │ │ ├── DailyTask.java │ │ │ ├── ChargeMe.java │ │ │ ├── GetVideoId.java │ │ │ ├── CoinAdd.java │ │ │ └── GiveGift.java │ │ │ ├── push │ │ │ ├── Push.java │ │ │ ├── model │ │ │ │ ├── PushMetaInfo.java │ │ │ │ ├── PushResult.java │ │ │ │ └── RetryContext.java │ │ │ ├── impl │ │ │ │ ├── TelegramPush.java │ │ │ │ ├── ServerChanPush.java │ │ │ │ ├── ServerChanTurboPush.java │ │ │ │ ├── DingTalkPush.java │ │ │ │ └── PushPlusPush.java │ │ │ ├── PushHelper.java │ │ │ └── AbstractPush.java │ │ │ ├── KeyValueClass.java │ │ │ ├── login │ │ │ ├── ServerVerify.java │ │ │ └── Verify.java │ │ │ ├── utils │ │ │ ├── VersionInfo.java │ │ │ ├── HelpUtil.java │ │ │ ├── LoadFileResource.java │ │ │ └── HttpUtil.java │ │ │ ├── apiquery │ │ │ ├── oftenAPI.java │ │ │ └── ApiList.java │ │ │ ├── config │ │ │ └── Config.java │ │ │ └── BiliMain.java │ │ └── org │ │ └── slf4j │ │ └── helpers │ │ └── Util.java └── test │ └── java │ ├── top │ └── misec │ │ └── task │ │ ├── PushTest.java │ │ └── UnitTest.java │ └── Log4j2Test.java ├── start.sh ├── .gitignore ├── LICENSE ├── setup.sh ├── scriptcat └── demo.js ├── pom.xml └── README.md /.github/trigger.json: -------------------------------------------------------------------------------- 1 | { 2 | "trigger": 0 3 | } 4 | -------------------------------------------------------------------------------- /docs/IMG/a.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/a.jpg -------------------------------------------------------------------------------- /docs/IMG/b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/b.jpg -------------------------------------------------------------------------------- /docs/IMG/c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/c.jpg -------------------------------------------------------------------------------- /docs/IMG/d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/d.jpg -------------------------------------------------------------------------------- /src/main/resources/release.json: -------------------------------------------------------------------------------- 1 | { 2 | "tag_main": "1.4.1", 3 | "tag_latest": "1.4.1" 4 | } -------------------------------------------------------------------------------- /docs/IMG/a0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/a0.jpg -------------------------------------------------------------------------------- /docs/IMG/qqgroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/qqgroup.png -------------------------------------------------------------------------------- /docs/IMG/openActions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/openActions.png -------------------------------------------------------------------------------- /docs/IMG/serverpush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/serverpush.png -------------------------------------------------------------------------------- /docs/IMG/20201012001307.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/20201012001307.png -------------------------------------------------------------------------------- /docs/IMG/20201013134942.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/20201013134942.png -------------------------------------------------------------------------------- /docs/IMG/20201013210000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/20201013210000.png -------------------------------------------------------------------------------- /docs/IMG/wechatMsgPush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/wechatMsgPush.png -------------------------------------------------------------------------------- /docs/IMG/workflow_dispatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alangsuo/BILIBILI-HELPER-PRE/HEAD/docs/IMG/workflow_dispatch.png -------------------------------------------------------------------------------- /src/main/resources/release.info: -------------------------------------------------------------------------------- 1 | 1.原仓库被GitHub关闭了,新仓库地址为: https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE 2 | 2.增加了对腾讯云函数的支持。 3 | 3.以前投币都是从热榜获取,目前投币有一定几率会投给项目贡献者。 4 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source /etc/profile 4 | source ~/.bashrc 5 | source ~/.zshrc #其他终端请自行引入环境变量 6 | echo $PATH 7 | java -jar /home/BILIBILI-HELPER.jar DEDEUSERID SESSDATA BILI_JCT SCKEY >> /var/log/bilibili-help.log 8 | # 注意将jar包路径替换为实际路径。将参数修改该你自己的参数,cookies中含有等特殊字符需要转义。 -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/rewardbean/JsonRootBean.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.rewardbean; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class JsonRootBean { 7 | 8 | private int code; 9 | private String message; 10 | private int ttl; 11 | private RewardData rewardData; 12 | 13 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/task/Task.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | /** 4 | * @author @Kurenai 5 | * @since 2020-11-22 5:22 6 | */ 7 | public interface Task { 8 | 9 | /** 10 | * 任务实现 11 | */ 12 | void run(); 13 | 14 | /** 15 | * 任务名 16 | * 17 | * @return taskName 18 | */ 19 | String getName(); 20 | 21 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/userinfobean/Vip_label.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.userinfobean; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Auto-generated 7 | * 8 | * @author Junzhou Liu 9 | * @create 2020/10/11 4:21 10 | */ 11 | @Data 12 | public class Vip_label { 13 | 14 | private String path; 15 | private String text; 16 | private String label_theme; 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/userinfobean/JsonRootBean.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.userinfobean; 2 | 3 | /** 4 | * Auto-generated 5 | * 6 | * @author Junzhou Liu 7 | * @create 2020/10/11 4:21 8 | */ 9 | 10 | @lombok.Data 11 | public class JsonRootBean { 12 | 13 | private int code; 14 | private String message; 15 | private int ttl; 16 | private Data data; 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/userinfobean/Official.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.userinfobean; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Auto-generated 7 | * 8 | * @author Junzhou Liu 9 | * @create 2020/10/11 4:21 10 | */ 11 | @Data 12 | public class Official { 13 | 14 | private int role; 15 | private String title; 16 | private String desc; 17 | private int type; 18 | 19 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/userinfobean/OfficialVerify.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.userinfobean; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Auto-generated: 2020-10-11 3:42:3 7 | * 8 | * @author bejson.com (i@bejson.com) 9 | * @website http://www.bejson.com/java2pojo/ 10 | */ 11 | @Data 12 | public class OfficialVerify { 13 | 14 | private int type; 15 | private String desc; 16 | 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/userinfobean/Wallet.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.userinfobean; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Auto-generated 7 | * 8 | * @author Junzhou Liu 9 | * @create 2020/10/11 4:21 10 | */ 11 | @Data 12 | public class Wallet { 13 | 14 | private long mid; 15 | private int bcoin_balance; 16 | private int coupon_balance; 17 | private int coupon_due_time; 18 | 19 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/rewardbean/RewardData.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.rewardbean; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class RewardData { 7 | 8 | private boolean login; 9 | private boolean watch; 10 | private int coins; 11 | private boolean share; 12 | private boolean email; 13 | private boolean tel; 14 | private boolean safe_question; 15 | private boolean identify_card; 16 | 17 | 18 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/userinfobean/Pendant.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.userinfobean; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Auto-generated 7 | * 8 | * @author Junzhou Liu 9 | * @create 2020/10/11 4:21 10 | */ 11 | 12 | @Data 13 | public class Pendant { 14 | 15 | private int pid; 16 | private String name; 17 | private String image; 18 | private int expire; 19 | private String image_enhance; 20 | 21 | 22 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/task/ExtensionMethod.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * @author Junzhou 10 | */ 11 | @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) 12 | @Retention(RetentionPolicy.CLASS) 13 | public @interface ExtensionMethod { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | .DS_Store 4 | 5 | # Log file 6 | *.log 7 | 8 | # BlueJ files 9 | *.ctxt 10 | 11 | # Mobile Tools for Java (J2ME) 12 | .mtj.tmp/ 13 | 14 | # Package Files # 15 | *.jar 16 | *.war 17 | *.nar 18 | *.ear 19 | *.zip 20 | *.tar.gz 21 | *.rar 22 | 23 | # IDEA 24 | *.iml 25 | .idea/ 26 | *.ipr 27 | *.iws 28 | 29 | /target/ 30 | 31 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 32 | hs_err_pid* 33 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/Push.java: -------------------------------------------------------------------------------- 1 | package top.misec.push; 2 | 3 | import top.misec.push.model.PushMetaInfo; 4 | import top.misec.push.model.PushResult; 5 | 6 | /** 7 | * 推送消息接口 8 | * 9 | * @author itning 10 | * @since 2021/3/22 16:36 11 | */ 12 | @FunctionalInterface 13 | public interface Push { 14 | /** 15 | * 发起推送 16 | * 17 | * @param metaInfo 元信息 18 | * @param content 推送内容 19 | * @return 推送结果 20 | */ 21 | PushResult doPush(PushMetaInfo metaInfo, String content); 22 | } 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug报告 about: Create a report to help us improve title: '' 3 | labels: '' 4 | assignees: '' 5 | 6 | --- 7 | 8 | --- 9 | name: "[Bug Report]BUG反馈" 10 | about: 将你所遇到的BUG反馈给我们 title: "[BUG]" 11 | labels: '' 12 | assignees: '' 13 | 14 | --- 15 | 16 | ### 错误描述 17 | 18 | > 你所遇到的错误是怎么样的? 19 | 20 | ### 错误提示和日志内容 21 | 22 | > 最好提供错误格式和日志,如果不熟悉编程语言,请提供完整的日志。 23 | 24 | ### 使用的版本信息 25 | 26 | > 提供出现这个问题的程序版本号,或着git hash 27 | 28 | ### 使用环境 29 | 30 | > 是在什么环境下出现的这个问题,Actions,Linux还是windows或者其他 31 | -------------------------------------------------------------------------------- /src/main/resources/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "numberOfCoins": 5, 3 | "reserveCoins": 50, 4 | "selectLike": 0, 5 | "monthEndAutoCharge": true, 6 | "giveGift": true, 7 | "upLive": "0", 8 | "chargeForLove": "0", 9 | "devicePlatform": "ios", 10 | "coinAddPriority": 1, 11 | "skipDailyTask": true, 12 | "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15", 13 | "dedeuserid": "", 14 | "sessdata": "", 15 | "biliJct": "", 16 | "telegrambottoken": null, 17 | "telegramchatid": null, 18 | "serverpushkey": null 19 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/userinfobean/Level_info.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.userinfobean; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Auto-generated 7 | * 8 | * @author Junzhou Liu 9 | * @create 2020/10/11 4:21 10 | */ 11 | 12 | @Data 13 | public class Level_info { 14 | 15 | private int current_level; 16 | private int current_min; 17 | private int current_exp; 18 | private String next_exp; 19 | 20 | public int getNext_exp_asInt() { 21 | if ("--".equals(next_exp)) { 22 | return current_exp; 23 | } else { 24 | return Integer.parseInt(next_exp); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/push/model/PushMetaInfo.java: -------------------------------------------------------------------------------- 1 | package top.misec.push.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | /** 7 | * 元信息 8 | * 9 | * @author itning 10 | * @since 2021/3/22 16:58 11 | */ 12 | @Getter 13 | @Builder 14 | public class PushMetaInfo { 15 | 16 | /** 17 | * TOKEN 18 | */ 19 | private final String token; 20 | 21 | /** 22 | * Telegram Chat Id 23 | */ 24 | private final String chatId; 25 | 26 | /** 27 | * 失败后重试次数 28 | */ 29 | private final int numberOfRetries; 30 | 31 | /** 32 | * 失败后重试间隔 33 | */ 34 | private final long retryInterval; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/top/misec/task/PushTest.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import top.misec.push.PushHelper; 4 | import top.misec.push.model.PushMetaInfo; 5 | 6 | /** 7 | * @author itning 8 | * @since 2021/3/28 16:06 9 | */ 10 | public class PushTest { 11 | public static void main(String[] args) { 12 | PushMetaInfo metaInfo = PushMetaInfo 13 | .builder() 14 | .numberOfRetries(3) 15 | .token(args.length > 0 ? args[0] : null) 16 | .chatId(args.length > 1 ? args[1] : null) 17 | .build(); 18 | PushHelper.push(PushHelper.Target.PUSH_PLUS, metaInfo, "测试内容"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request about: Suggest an idea for this project title: '' 3 | labels: '' 4 | assignees: '' 5 | 6 | --- 7 | 8 | **Is your feature request related to a problem? Please describe.** 9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 10 | 11 | **Describe the solution you'd like** 12 | A clear and concise description of what you want to happen. 13 | 14 | **Describe alternatives you've considered** 15 | A clear and concise description of any alternative solutions or features you've considered. 16 | 17 | **Additional context** 18 | Add any other context or screenshots about the feature request here. 19 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/model/PushResult.java: -------------------------------------------------------------------------------- 1 | package top.misec.push.model; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author itning 7 | * @since 2021/3/22 16:58 8 | */ 9 | @Getter 10 | public class PushResult { 11 | 12 | private final boolean success; 13 | 14 | public PushResult(boolean success) { 15 | this.success = success; 16 | } 17 | 18 | public static PushResult success() { 19 | return new PushResult(true); 20 | } 21 | 22 | public static PushResult failed() { 23 | return new PushResult(false); 24 | } 25 | 26 | public static boolean checkSuccess(PushResult pushResult) { 27 | return null != pushResult && pushResult.isSuccess(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/top/misec/KeyValueClass.java: -------------------------------------------------------------------------------- 1 | package top.misec; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 外部配置 7 | * 8 | * @author itning 9 | * @since 2021/4/29 17:55 10 | */ 11 | @Data 12 | public class KeyValueClass { 13 | private String dedeuserid; 14 | private String sessdata; 15 | private String biliJct; 16 | private String serverpushkey; 17 | private String telegrambottoken; 18 | private String telegramchatid; 19 | 20 | private int numberOfCoins; 21 | private int reserveCoins; 22 | private int selectLike; 23 | private boolean monthEndAutoCharge; 24 | private boolean giveGift; 25 | private String upLive; 26 | private String chargeForLove; 27 | private String devicePlatform; 28 | private int coinAddPriority; 29 | private boolean skipDailyTask; 30 | private String userAgent; 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/Log4j2Test.java: -------------------------------------------------------------------------------- 1 | import org.apache.logging.log4j.LogManager; 2 | import org.apache.logging.log4j.core.Logger; 3 | import org.junit.Test; 4 | import top.misec.task.ServerPush; 5 | 6 | public class Log4j2Test { 7 | 8 | static Logger LOG = (Logger) LogManager.getLogger(Log4j2Test.class.getName()); 9 | 10 | public static void main(String[] args) throws Exception { 11 | System.out.println(); 12 | ServerPush serverPush = new ServerPush(); 13 | // serverPush.setPushToken(args[0]); 14 | } 15 | 16 | // 打印各种级别的日志用于测试 17 | @Test 18 | public void logAll() throws Exception { 19 | LOG.trace("trace level log"); 20 | LOG.debug("debug level log"); 21 | LOG.info("info level log"); 22 | LOG.error("error level log"); 23 | LOG.fatal("fatal level log"); 24 | // 设置休眠时间(单位ms),控制日志打印速度 25 | Thread.sleep(3); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/top/misec/login/ServerVerify.java: -------------------------------------------------------------------------------- 1 | package top.misec.login; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * @author Junzhou Liu 7 | * @create 2020/10/21 19:57 8 | */ 9 | @Getter 10 | public class ServerVerify { 11 | 12 | private final static ServerVerify SERVER_VERIFY = new ServerVerify(); 13 | private static String FTKEY = null; 14 | private static String CHATID = null; 15 | 16 | public static void verifyInit(String ftKey) { 17 | ServerVerify.FTKEY = ftKey; 18 | } 19 | 20 | public static void verifyInit(String ftKey, String chatId) { 21 | ServerVerify.FTKEY = ftKey; 22 | ServerVerify.CHATID = chatId; 23 | } 24 | 25 | public static String getFtkey() { 26 | return FTKEY; 27 | } 28 | 29 | public static String getChatId() { 30 | return CHATID; 31 | } 32 | 33 | public static ServerVerify getInstance() { 34 | return SERVER_VERIFY; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/MangaSign.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.config.Config; 7 | import top.misec.utils.HttpUtil; 8 | 9 | /** 10 | * 漫画签到 11 | * 12 | * @author @JunzhouLiu @Kurenai 13 | * @since 2020-11-22 5:22 14 | */ 15 | 16 | @Slf4j 17 | public class MangaSign implements Task { 18 | 19 | 20 | private final String taskName = "漫画签到"; 21 | 22 | @Override 23 | public void run() { 24 | 25 | String platform = Config.getInstance().getDevicePlatform(); 26 | String requestBody = "platform=" + platform; 27 | JsonObject result = HttpUtil.doPost(ApiList.Manga, requestBody); 28 | 29 | if (result == null) { 30 | log.info("哔哩哔哩漫画已经签到过了"); 31 | } else { 32 | log.info("完成漫画签到"); 33 | } 34 | } 35 | 36 | @Override 37 | public String getName() { 38 | return taskName; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/resources/logging.properties: -------------------------------------------------------------------------------- 1 | # RootLogger 顶级父元素指定的默认处理器为:ConsoleHandler 2 | handlers=java.util.logging.ConsoleHandler,java.util.logging.FileHandler 3 | # RootLogger 顶级父元素默认的日志级别为:ALL 4 | .level=DEBUG 5 | org.apache.http.level=OFF 6 | # 关闭默认配置 7 | # 向日志文件输出的 handler 对象 8 | # 指定日志文件路径 /tmp/daily.log 9 | java.util.logging.FileHandler.pattern=%t/daily.log 10 | # 指定日志文件内容大小 11 | java.util.logging.FileHandler.limit=50000 12 | # 指定日志文件数量 13 | java.util.logging.FileHandler.count=1 14 | # 指定 handler 对象日志消息格式对象 15 | java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter 16 | # 指定以追加方式添加日志内容 17 | java.util.logging.FileHandler.append=true 18 | # 向控制台输出的 handler 对象 19 | # 指定 handler 对象的日志级别 20 | java.util.logging.ConsoleHandler.level=ALL 21 | # 指定 handler 对象的日志消息格式对象 22 | java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter 23 | # 指定 handler 对象的字符集 24 | java.util.logging.ConsoleHandler.encoding=UTF-8 25 | # 指定日志消息格式 26 | # [%1$tc][%2$s]%4$s: %5$s%6$s%n 27 | java.util.logging.SimpleFormatter.format=%5$s%6$s%n -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Junzhou Liu 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 | -------------------------------------------------------------------------------- /src/test/java/top/misec/task/UnitTest.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import lombok.extern.log4j.Log4j2; 4 | import org.junit.Test; 5 | import top.misec.config.Config; 6 | import top.misec.login.ServerVerify; 7 | import top.misec.login.Verify; 8 | import top.misec.utils.VersionInfo; 9 | 10 | /** 11 | * @author Junzhou Liu 12 | * @create 2021/1/15 23:16 13 | */ 14 | @Log4j2 15 | public class UnitTest { 16 | @Test 17 | public static void main(String[] args) { 18 | if (args.length < 3) { 19 | log.info("任务启动失败"); 20 | log.warn("Cookies参数缺失,请检查是否在Github Secrets中配置Cookies参数"); 21 | } 22 | //读取环境变量 23 | Verify.verifyInit(args[0], args[1], args[2]); 24 | 25 | if (args.length > 4) { 26 | ServerVerify.verifyInit(args[3], args[4]); 27 | } else if (args.length > 3) { 28 | ServerVerify.verifyInit(args[3]); 29 | } 30 | 31 | VersionInfo.printVersionInfo(); 32 | //每日任务65经验 33 | 34 | //初始化配置 35 | Config.getInstance().configInit(); 36 | 37 | new UserCheck().run(); 38 | ServerPush.doServerPush(); 39 | //new CoinAdd().run(); 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/top/misec/pojo/userinfobean/Data.java: -------------------------------------------------------------------------------- 1 | package top.misec.pojo.userinfobean; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | /** 7 | * Auto-generated 8 | * 9 | * @author Junzhou Liu 10 | * @create 2020/10/11 4:21 11 | */ 12 | @Getter 13 | @Setter 14 | public class Data { 15 | 16 | private boolean isLogin; 17 | private int email_verified; 18 | private String face; 19 | private Level_info level_info; 20 | private long mid; 21 | private int mobile_verified; 22 | private double money; 23 | private int moral; 24 | private Official official; 25 | private OfficialVerify officialVerify; 26 | private Pendant pendant; 27 | private int scores; 28 | private String uname; 29 | private long vipDueDate; 30 | private int vipStatus; 31 | private int vipType; 32 | private int vip_pay_type; 33 | private int vip_theme_type; 34 | private Vip_label vip_label; 35 | private int vip_avatar_subscript; 36 | private String vip_nickname_color; 37 | private Wallet wallet; 38 | private boolean has_shop; 39 | private String shop_url; 40 | private int allowance_count; 41 | private int answer_status; 42 | 43 | 44 | } -------------------------------------------------------------------------------- /src/main/java/top/misec/push/impl/TelegramPush.java: -------------------------------------------------------------------------------- 1 | package top.misec.push.impl; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.push.AbstractPush; 7 | import top.misec.push.model.PushMetaInfo; 8 | 9 | /** 10 | * TG推送 11 | * 12 | * @author itning 13 | * @since 2021/3/22 17:55 14 | */ 15 | public class TelegramPush extends AbstractPush { 16 | 17 | @Override 18 | protected String generatePushUrl(PushMetaInfo metaInfo) { 19 | return ApiList.ServerPushTelegram + metaInfo.getToken() + "/sendMessage"; 20 | } 21 | 22 | @Override 23 | protected boolean checkPushStatus(JsonObject jsonObject) { 24 | if (null == jsonObject) { 25 | return false; 26 | } 27 | 28 | JsonElement ok = jsonObject.get("ok"); 29 | if (null == ok) { 30 | return false; 31 | } 32 | 33 | return "true".equals(ok.getAsString()); 34 | } 35 | 36 | @Override 37 | protected String generatePushBody(PushMetaInfo metaInfo, String content) { 38 | return "chat_id=" + metaInfo.getChatId() + "&text=BILIBILI-HELPER任务简报\n" + content; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/LiveCheckin.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.utils.HttpUtil; 7 | 8 | import static top.misec.task.TaskInfoHolder.STATUS_CODE_STR; 9 | 10 | /** 11 | * 直播签到 12 | * 13 | * @author @JunzhouLiu @Kurenai 14 | * @since 2020-11-22 5:42 15 | */ 16 | @Slf4j 17 | public class LiveCheckin implements Task { 18 | 19 | 20 | private final String taskName = "直播签到"; 21 | 22 | @Override 23 | public void run() { 24 | JsonObject liveCheckinResponse = HttpUtil.doGet(ApiList.liveCheckin); 25 | int code = liveCheckinResponse.get(STATUS_CODE_STR).getAsInt(); 26 | if (code == 0) { 27 | JsonObject data = liveCheckinResponse.get("data").getAsJsonObject(); 28 | log.info("直播签到成功,本次签到获得" + data.get("text").getAsString() + "," + data.get("specialText").getAsString()); 29 | } else { 30 | String message = liveCheckinResponse.get("message").getAsString(); 31 | log.debug("直播签到失败: " + message); 32 | } 33 | } 34 | 35 | @Override 36 | public String getName() { 37 | return taskName; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/impl/ServerChanPush.java: -------------------------------------------------------------------------------- 1 | package top.misec.push.impl; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.push.AbstractPush; 7 | import top.misec.push.model.PushMetaInfo; 8 | 9 | /** 10 | * server酱推送 11 | * 12 | * @author itning 13 | * @since 2021/3/22 16:37 14 | * @deprecated Server酱旧版推送渠道即将下线,使用Turbo版本{@link ServerChanTurboPush}替代 15 | */ 16 | @Deprecated 17 | public class ServerChanPush extends AbstractPush { 18 | 19 | @Override 20 | protected String generatePushUrl(PushMetaInfo metaInfo) { 21 | return ApiList.ServerPush + metaInfo.getToken() + ".send"; 22 | } 23 | 24 | @Override 25 | protected boolean checkPushStatus(JsonObject jsonObject) { 26 | if (null == jsonObject) { 27 | return false; 28 | } 29 | 30 | JsonElement code = jsonObject.get("code"); 31 | 32 | if (null == code) { 33 | return false; 34 | } 35 | 36 | return code.getAsInt() == 0; 37 | } 38 | 39 | @Override 40 | protected String generatePushBody(PushMetaInfo metaInfo, String content) { 41 | return "text=BILIBILI-HELPER任务简报&desp=" + content; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/top/misec/login/Verify.java: -------------------------------------------------------------------------------- 1 | package top.misec.login; 2 | 3 | 4 | /** 5 | * @author Junzhou Liu 6 | * @create 2020/10/11 16:49 7 | */ 8 | public class Verify { 9 | 10 | private final static Verify VERIFY = new Verify(); 11 | private static String userId = ""; 12 | private static String sessData = ""; 13 | private static String biliJct = ""; 14 | 15 | public Verify() { 16 | 17 | } 18 | 19 | /** 20 | * Cookies信息 从浏览器获取 21 | * 22 | * @param userId uid 23 | * @param sessData sessData 24 | * @param biliJct biliJct or CSRF 25 | */ 26 | public static void verifyInit(String userId, String sessData, String biliJct) { 27 | Verify.userId = userId; 28 | Verify.sessData = sessData; 29 | Verify.biliJct = biliJct; 30 | } 31 | 32 | 33 | public static Verify getInstance() { 34 | return VERIFY; 35 | } 36 | 37 | public String getUserId() { 38 | return userId; 39 | } 40 | 41 | public String getSessData() { 42 | return sessData; 43 | } 44 | 45 | public String getBiliJct() { 46 | return biliJct; 47 | } 48 | 49 | public String getVerify() { 50 | return "bili_jct=" + getBiliJct() + ";SESSDATA=" + getSessData() + ";DedeUserID=" + getUserId() + ";"; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/top/misec/utils/VersionInfo.java: -------------------------------------------------------------------------------- 1 | package top.misec.utils; 2 | 3 | import com.google.gson.JsonObject; 4 | import com.google.gson.JsonParser; 5 | import lombok.Data; 6 | import lombok.extern.slf4j.Slf4j; 7 | 8 | /** 9 | * @author Junzhou Liu 10 | * @create 2020/11/21 15:22 11 | */ 12 | @Slf4j 13 | @Data 14 | public class VersionInfo { 15 | private static String releaseVersion = ""; 16 | private static String updateDate = "2021-04-28"; 17 | private static String projectRepo = "https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE"; 18 | private static String releaseInfo = ""; 19 | 20 | public static void initInfo() { 21 | String release = LoadFileResource.loadJsonFromAsset("release.json"); 22 | JsonObject jsonObject = new JsonParser().parse(release).getAsJsonObject(); 23 | releaseVersion = jsonObject.get("tag_main").getAsString(); 24 | releaseInfo = LoadFileResource.loadJsonFromAsset("release.info"); 25 | 26 | } 27 | 28 | public static void printVersionInfo() { 29 | initInfo(); 30 | log.info("-----版本信息-----"); 31 | log.info("当前版本: " + releaseVersion); 32 | log.info("版本更新内容: " + releaseInfo); 33 | log.info("最后更新日期: " + updateDate); 34 | log.info("项目开源地址: " + projectRepo); 35 | log.info("-----版本信息-----\n"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/MangaRead.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.utils.HttpUtil; 7 | 8 | import static top.misec.task.TaskInfoHolder.STATUS_CODE_STR; 9 | 10 | /** 11 | * @author Junzhou Liu 12 | * @create 2021/1/13 17:50 13 | */ 14 | @Slf4j 15 | public class MangaRead implements Task { 16 | 17 | @Override 18 | public void run() { 19 | String urlParam = "?device=pc&platform=web"; 20 | String requestBody = "comic_id=27355" + 21 | "&ep_id=381662"; 22 | JsonObject jsonObject = new JsonObject(); 23 | jsonObject.addProperty("device", "pc"); 24 | jsonObject.addProperty("platform", "web"); 25 | jsonObject.addProperty("comic_id", "27355"); 26 | jsonObject.addProperty("ep_id", "381662"); 27 | 28 | JsonObject result = HttpUtil.doPost(ApiList.mangaRead + urlParam, jsonObject); 29 | int code = result.get(STATUS_CODE_STR).getAsInt(); 30 | if (code == 0) { 31 | log.info("本日漫画自动阅读1章节成功!,阅读漫画为:堀与宫村"); 32 | } else { 33 | log.debug("阅读失败,错误信息为\n```json\n{}\n```", result); 34 | } 35 | 36 | } 37 | 38 | @Override 39 | public String getName() { 40 | return "每日漫画阅读"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/model/RetryContext.java: -------------------------------------------------------------------------------- 1 | package top.misec.push.model; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.concurrent.TimeUnit; 6 | 7 | /** 8 | * @author itning 9 | * @since 2021/3/22 17:25 10 | */ 11 | @Getter 12 | public class RetryContext { 13 | 14 | /** 15 | * 推送URL 16 | */ 17 | private final String url; 18 | 19 | /** 20 | * 推送请求体内容 21 | */ 22 | private final String body; 23 | 24 | /** 25 | * 失败后重试次数 26 | */ 27 | private final int numberOfRetries; 28 | 29 | /** 30 | * 失败后重试间隔(毫秒) 31 | */ 32 | private final long retryInterval; 33 | 34 | /** 35 | * 重试计数器 36 | */ 37 | private int retryCount; 38 | 39 | public RetryContext(String url, String body, int numberOfRetries, long retryInterval) { 40 | this.url = url; 41 | this.body = body; 42 | this.numberOfRetries = numberOfRetries; 43 | this.retryInterval = retryInterval; 44 | } 45 | 46 | public boolean next() { 47 | if (numberOfRetries <= 0) { 48 | return false; 49 | } 50 | if (++retryCount > numberOfRetries) { 51 | return false; 52 | } 53 | if (retryInterval > 0) { 54 | try { 55 | TimeUnit.MILLISECONDS.sleep(retryInterval); 56 | } catch (InterruptedException ignored) { 57 | } 58 | } 59 | return true; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/impl/ServerChanTurboPush.java: -------------------------------------------------------------------------------- 1 | package top.misec.push.impl; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import lombok.extern.slf4j.Slf4j; 6 | import top.misec.apiquery.ApiList; 7 | import top.misec.push.AbstractPush; 8 | import top.misec.push.model.PushMetaInfo; 9 | 10 | /** 11 | * Turbo版本server酱推送 12 | * 13 | * @author itning 14 | * @since 2021/3/22 17:14 15 | */ 16 | @Slf4j 17 | public class ServerChanTurboPush extends AbstractPush { 18 | 19 | @Override 20 | protected String generatePushUrl(PushMetaInfo metaInfo) { 21 | return ApiList.ServerPushV2 + metaInfo.getToken() + ".send"; 22 | } 23 | 24 | @Override 25 | protected boolean checkPushStatus(JsonObject jsonObject) { 26 | if (null == jsonObject) { 27 | return false; 28 | } 29 | // {"code":0,"message":"","data":{"pushid":"XXX","readkey":"XXX","error":"SUCCESS","errno":0}} 30 | JsonElement code = jsonObject.get("code"); 31 | 32 | if (null == code) { 33 | return false; 34 | } 35 | 36 | // FIX #380 37 | switch (code.getAsInt()) { 38 | case 0: 39 | return true; 40 | case 40001: 41 | log.info("超过当天的发送次数限制[10],请稍后再试"); 42 | return true; 43 | default: 44 | return code.getAsInt() == 0; 45 | } 46 | } 47 | 48 | @Override 49 | protected String generatePushBody(PushMetaInfo metaInfo, String content) { 50 | return "title=BILIBILI-HELPER任务简报&desp=" + content; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | version="1.4.1" 3 | 4 | function installJava(){ 5 | command -v apt >/dev/null 2>&1 && (apt-get update; apt-get install openjdk-8-jdk -y; return;) 6 | command -v yum >/dev/null 2>&1 && (yum install java-1.8.0-openjdk -y; return;) 7 | } 8 | 9 | function installUnzip(){ 10 | command -v apt >/dev/null 2>&1 && (apt-get update; apt-get install unzip -y; return;) 11 | command -v yum >/dev/null 2>&1 && (yum install unzip -y; return;) 12 | } 13 | 14 | function download(){ 15 | wget -O "/tmp/BILIBILI-HELPER.zip" "https://glare.now.sh/JunzhouLiu/BILIBILI-HELPER/BILIBILI-HELPER-v${1}.zip" 16 | mkdir "${HOME}/BILIBILI-HELPER" 17 | command -v unzip >/dev/null 2>&1 || installUnzip 18 | unzip -o "/tmp/BILIBILI-HELPER.zip" -d "${HOME}/BILIBILI-HELPER" 19 | mv "${HOME}/BILIBILI-HELPER/BILIBILI-HELPER-v${1}.jar" "${HOME}/BILIBILI-HELPER/BILIBILI-HELPER.jar" -f 20 | } 21 | 22 | function setCron(){ 23 | file="/var/spool/cron/${USER}" 24 | if [ ! -f "$file" ]; then 25 | touch "$file" 26 | else 27 | find=`grep "BILIBILI-HELPER" "$file"` 28 | if [ -z "$find" ]; then 29 | echo "" >> "$file" 30 | echo "30 10 * * * cd ${HOME}/BILIBILI-HELPER; java -jar ./BILIBILI-HELPER.jar ${1} ${2} ${3} ${4} >>/var/log/cron.log 2>&1 &" >> "$file" 31 | service crond reload 32 | service cron reload 33 | fi 34 | fi 35 | } 36 | 37 | read -p "请粘贴SESSDATA并回车:" SESSDATA 38 | read -p "请粘贴DEDEUSERID并回车:" DEDEUSERID 39 | read -p "请粘贴BILI_JCT并回车:" BILI_JCT 40 | read -p "请粘贴SCKEY并回车:" SCKEY 41 | 42 | download $version 43 | setCron "${DEDEUSERID}" "${SESSDATA}" "${BILI_JCT}" "${SCKEY}" 44 | command -v java >/dev/null 2>&1 || installJava 45 | 46 | echo "执行完成" 47 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/impl/DingTalkPush.java: -------------------------------------------------------------------------------- 1 | package top.misec.push.impl; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import lombok.Getter; 7 | import top.misec.push.AbstractPush; 8 | import top.misec.push.model.PushMetaInfo; 9 | 10 | /** 11 | * 钉钉机器人 12 | * 13 | * @author itning 14 | * @since 2021/3/22 19:15 15 | */ 16 | public class DingTalkPush extends AbstractPush { 17 | 18 | @Override 19 | protected String generatePushUrl(PushMetaInfo metaInfo) { 20 | return metaInfo.getToken(); 21 | } 22 | 23 | @Override 24 | protected boolean checkPushStatus(JsonObject jsonObject) { 25 | if (jsonObject == null) { 26 | return false; 27 | } 28 | JsonElement errcode = jsonObject.get("errcode"); 29 | JsonElement errmsg = jsonObject.get("errmsg"); 30 | if (null == errcode || null == errmsg) { 31 | return false; 32 | } 33 | return errcode.getAsInt() == 0 && "ok".equals(errmsg.getAsString()); 34 | } 35 | 36 | @Override 37 | protected String generatePushBody(PushMetaInfo metaInfo, String content) { 38 | return new Gson().toJson(new MessageModel(content)); 39 | } 40 | 41 | @Getter 42 | static class MessageModel { 43 | private final String msgtype = "text"; 44 | private final String title = "BILIBILI-HELPER任务简报"; 45 | private final Text text; 46 | 47 | public MessageModel(String content) { 48 | this.text = new Text(content); 49 | } 50 | } 51 | 52 | @Getter 53 | static class Text { 54 | private final String content; 55 | 56 | public Text(String content) { 57 | this.content = content.replaceAll("\r\n\r", ""); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/impl/PushPlusPush.java: -------------------------------------------------------------------------------- 1 | package top.misec.push.impl; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import lombok.Getter; 7 | import top.misec.apiquery.ApiList; 8 | import top.misec.push.AbstractPush; 9 | import top.misec.push.model.PushMetaInfo; 10 | 11 | /** 12 | * Push Plus 推送 13 | * 14 | * @author itning 15 | * @since 2021/3/28 15:49 16 | */ 17 | public class PushPlusPush extends AbstractPush { 18 | 19 | /** 20 | * Push ++ 默认TOKEN长度 21 | */ 22 | public static final int PUSH_PLUS_CHANNEL_TOKEN_DEFAULT_LENGTH = 32; 23 | 24 | @Override 25 | protected String generatePushUrl(PushMetaInfo metaInfo) { 26 | return ApiList.PushPlus; 27 | } 28 | 29 | @Override 30 | protected boolean checkPushStatus(JsonObject jsonObject) { 31 | if (null == jsonObject) { 32 | return false; 33 | } 34 | 35 | // See https://www.pushplus.plus/doc/guide/api.htm 36 | JsonElement code = jsonObject.get("code"); 37 | 38 | if (code == null) { 39 | return false; 40 | } 41 | 42 | return code.getAsInt() == 200; 43 | } 44 | 45 | @Override 46 | protected String generatePushBody(PushMetaInfo metaInfo, String content) { 47 | return new Gson().toJson(new PushModel(metaInfo.getToken(), content)); 48 | } 49 | 50 | @Getter 51 | static class PushModel { 52 | private final String title = "BILIBILI-HELPER任务简报"; 53 | private final String template = "markdown"; 54 | private final String token; 55 | private final String content; 56 | 57 | public PushModel(String token, String content) { 58 | this.token = token; 59 | this.content = content; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/PushHelper.java: -------------------------------------------------------------------------------- 1 | package top.misec.push; 2 | 3 | import top.misec.push.impl.*; 4 | import top.misec.push.model.PushMetaInfo; 5 | 6 | /** 7 | * 推送工具 8 | * 9 | * @author itning 10 | * @since 2021/3/22 17:51 11 | */ 12 | public final class PushHelper { 13 | 14 | /** 15 | * 推送 16 | * 17 | * @param target 目标 18 | * @param metaInfo 元信息 19 | * @param content 内容 20 | * @return 推送结果 21 | */ 22 | public static boolean push(Target target, PushMetaInfo metaInfo, String content) { 23 | 24 | switch (target) { 25 | case SERVER_CHAN: { 26 | return new ServerChanPush().doPush(metaInfo, content).isSuccess(); 27 | } 28 | case SERVER_CHAN_TURBO: { 29 | return new ServerChanTurboPush().doPush(metaInfo, content).isSuccess(); 30 | } 31 | case TELEGRAM: { 32 | return new TelegramPush().doPush(metaInfo, content).isSuccess(); 33 | } 34 | case DING_TALK: { 35 | return new DingTalkPush().doPush(metaInfo, content).isSuccess(); 36 | } 37 | case PUSH_PLUS: { 38 | return new PushPlusPush().doPush(metaInfo, content).isSuccess(); 39 | } 40 | default: 41 | return false; 42 | } 43 | } 44 | 45 | /** 46 | * 推送目标 47 | */ 48 | public enum Target { 49 | /** 50 | * server酱 51 | */ 52 | SERVER_CHAN, 53 | /** 54 | * server酱turbo版 55 | */ 56 | SERVER_CHAN_TURBO, 57 | /** 58 | * TG 59 | */ 60 | TELEGRAM, 61 | /** 62 | * 钉钉 63 | */ 64 | DING_TALK, 65 | 66 | /** 67 | * Push Plus 68 | */ 69 | PUSH_PLUS 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/UserCheck.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonObject; 5 | import lombok.extern.slf4j.Slf4j; 6 | import top.misec.apiquery.ApiList; 7 | import top.misec.pojo.userinfobean.Data; 8 | import top.misec.utils.HelpUtil; 9 | import top.misec.utils.HttpUtil; 10 | 11 | import static top.misec.task.TaskInfoHolder.STATUS_CODE_STR; 12 | import static top.misec.task.TaskInfoHolder.userInfo; 13 | 14 | /** 15 | * 登录检查 16 | * 17 | * @author @JunzhouLiu @Kurenai 18 | * @since 2020-11-22 4:57 19 | */ 20 | @Slf4j 21 | public class UserCheck implements Task { 22 | 23 | private final String taskName = "登录检查"; 24 | 25 | @Override 26 | public void run() { 27 | String requestPram = ""; 28 | JsonObject userJson = HttpUtil.doGet(ApiList.LOGIN + requestPram); 29 | if (userJson == null) { 30 | log.info("用户信息请求失败,如果是412错误,请在config.json中更换UA,412问题仅影响用户信息确认,不影响任务"); 31 | } else { 32 | userJson = HttpUtil.doGet(ApiList.LOGIN); 33 | //判断Cookies是否有效 34 | if (userJson.get(STATUS_CODE_STR).getAsInt() == 0 35 | && userJson.get("data").getAsJsonObject().get("isLogin").getAsBoolean()) { 36 | userInfo = new Gson().fromJson(userJson 37 | .getAsJsonObject("data"), Data.class); 38 | log.info("Cookies有效,登录成功"); 39 | } else { 40 | log.debug(String.valueOf(userJson)); 41 | log.warn("Cookies可能失效了,请仔细检查Github Secrets中DEDEUSERID SESSDATA BILI_JCT三项的值是否正确、过期"); 42 | } 43 | 44 | log.info("用户名称: {}", HelpUtil.userNameEncode(userInfo.getUname())); 45 | log.info("硬币余额: " + userInfo.getMoney()); 46 | } 47 | 48 | } 49 | 50 | @Override 51 | public String getName() { 52 | return taskName; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/Silver2coin.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.apiquery.oftenAPI; 7 | import top.misec.utils.HttpUtil; 8 | 9 | import static top.misec.task.TaskInfoHolder.STATUS_CODE_STR; 10 | import static top.misec.task.TaskInfoHolder.userInfo; 11 | 12 | /** 13 | * 银瓜子换硬币 14 | * 15 | * @author @JunzhouLiu @Kurenai 16 | * @since 2020-11-22 5:25 17 | */ 18 | @Slf4j 19 | public class Silver2coin implements Task { 20 | 21 | @Override 22 | public void run() { 23 | 24 | JsonObject queryStatus = HttpUtil.doGet(ApiList.getSilver2coinStatus).get("data").getAsJsonObject(); 25 | //银瓜子兑换硬币汇率 26 | final int exchangeRate = 700; 27 | int silverNum = queryStatus.get("silver").getAsInt(); 28 | 29 | if (silverNum < exchangeRate) { 30 | log.info("当前银瓜子余额为:{},不足700,不进行兑换", silverNum); 31 | return; 32 | } else { 33 | JsonObject resultJson = HttpUtil.doGet(ApiList.silver2coin); 34 | int responseCode = resultJson.get(STATUS_CODE_STR).getAsInt(); 35 | if (responseCode == 0) { 36 | log.info("银瓜子兑换硬币成功"); 37 | 38 | double coinMoneyAfterSilver2Coin = oftenAPI.getCoinBalance(); 39 | 40 | log.info("当前银瓜子余额: {}", (silverNum - exchangeRate)); 41 | log.info("兑换银瓜子后硬币余额: {}", coinMoneyAfterSilver2Coin); 42 | 43 | //兑换银瓜子后,更新userInfo中的硬币值 44 | if (userInfo != null) { 45 | userInfo.setMoney(coinMoneyAfterSilver2Coin); 46 | } 47 | } else { 48 | log.info("银瓜子兑换硬币失败 原因是:{}", resultJson.get("msg").getAsString()); 49 | } 50 | } 51 | 52 | } 53 | 54 | @Override 55 | public String getName() { 56 | return "银瓜子换硬币"; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /scriptcat/demo.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name bilibili自动签到 3 | // @namespace wyz 4 | // @version 1.1.0 5 | // @author wyz 6 | // @crontab * * once * * 7 | // @debug 8 | // @grant GM_xmlhttpRequest 9 | // @grant GM_notification 10 | // @connect api.bilibili.com 11 | // @connect api.live.bilibili.com 12 | // @supportURL https://bbs.tampermonkey.net.cn/forum.php?mod=viewthread&tid=370 13 | // @homepage https://bbs.tampermonkey.net.cn/forum.php?mod=viewthread&tid=370 14 | // ==/UserScript== 15 | 16 | return new Promise((resolve, reject) => { 17 | GM_xmlhttpRequest({ 18 | method: 'GET', 19 | url: 'https://api.bilibili.com/x/web-interface/nav', 20 | onload: function (xhr) { 21 | 22 | switch(xhr.response.code){ 23 | case 0: 24 | GM_xmlhttpRequest({ 25 | method: 'GET', 26 | url: 'https://api.live.bilibili.com/sign/doSign', 27 | onload: function (xhr) { 28 | 29 | switch(xhr.response.code ){ 30 | case 0: 31 | GM_notification('哔哩哔哩直播自动签到成功'); 32 | resolve('B站签到完成'); 33 | break; 34 | case 1011040: 35 | GM_notification({ 36 | title: 'bilibili自动签到 - ScriptCat', 37 | text: '重复签到', 38 | }); 39 | break; 40 | default: 41 | } 42 | 43 | } 44 | }); 45 | break; 46 | case -101: 47 | GM_notification({ 48 | title: 'bilibili自动签到 - ScriptCat', 49 | text: '哔哩哔哩签到失败,账号未登录,请先登录', 50 | }); 51 | break; 52 | default: 53 | 54 | } 55 | 56 | 57 | } 58 | }); 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /src/main/java/top/misec/utils/HelpUtil.java: -------------------------------------------------------------------------------- 1 | package top.misec.utils; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | 6 | /** 7 | * @author Junzhou Liu 8 | * @create 2020/10/11 20:49 9 | */ 10 | public class HelpUtil { 11 | private static final String TABLE = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"; 12 | private static final HashMap MP = new HashMap<>(); 13 | private static final HashMap MP2 = new HashMap<>(); 14 | static int[] ss = {11, 10, 3, 8, 4, 6, 2, 9, 5, 7}; 15 | static long xor = 177451812; 16 | static long add = 8728348608L; 17 | 18 | public static void main(String[] args) { 19 | 20 | } 21 | 22 | public static long power(int a, int b) { 23 | long power = 1; 24 | for (int c = 0; c < b; c++) { 25 | power *= a; 26 | } 27 | 28 | return power; 29 | } 30 | 31 | public static String bv2av(String s) { 32 | long r = 0; 33 | for (int i = 0; i < 58; i++) { 34 | String s1 = TABLE.substring(i, i + 1); 35 | MP.put(s1, i); 36 | } 37 | for (int i = 0; i < 6; i++) { 38 | r = r + MP.get(s.substring(ss[i], ss[i] + 1)) * power(58, i); 39 | } 40 | return ((r - add) ^ xor) + ""; 41 | } 42 | 43 | public static String av2bv(String st) { 44 | long s = Long.parseLong(st.split("av")[1]); 45 | StringBuilder sb = new StringBuilder("BV1 4 1 7 "); 46 | s = (s ^ xor) + add; 47 | for (int i = 0; i < 58; i++) { 48 | String s1 = TABLE.substring(i, i + 1); 49 | MP2.put(i, s1); 50 | } 51 | for (int i = 0; i < 6; i++) { 52 | String r = MP2.get((int) (s / power(58, i) % 58)); 53 | sb.replace(ss[i], ss[i] + 1, r); 54 | } 55 | return sb.toString(); 56 | } 57 | 58 | public static String userNameEncode(String userName) { 59 | int s1 = userName.length() / 2, s2 = (s1 + 1) / 2; 60 | return userName.substring(0, s2) + String.join("", Collections.nCopies(s1, "*")) + 61 | userName.substring(s1 + s2); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/TaskInfoHolder.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.pojo.userinfobean.Data; 7 | import top.misec.utils.HttpUtil; 8 | 9 | /** 10 | * 任务信息持有类 11 | * 12 | * @author @JunzhouLiu @Kurenai 13 | * @since 2020-11-22 5:02 14 | */ 15 | @Slf4j 16 | public class TaskInfoHolder { 17 | 18 | public static final String STATUS_CODE_STR = "code"; 19 | public static Data userInfo = null; 20 | public static GetVideoId getVideoId = new GetVideoId(); 21 | 22 | public static void calculateUpgradeDays() { 23 | if (userInfo == null) { 24 | log.info("未请求到用户信息,暂无法计算等级相关数据"); 25 | return; 26 | } 27 | 28 | int todayExp = 15; 29 | todayExp += expConfirm() * 10; 30 | log.info("今日获得的总经验值为: " + todayExp); 31 | 32 | int needExp = userInfo.getLevel_info().getNext_exp_asInt() 33 | - userInfo.getLevel_info().getCurrent_exp(); 34 | 35 | if (userInfo.getLevel_info().getCurrent_level() < 6) { 36 | log.info("按照当前进度,升级到升级到Lv" + (userInfo.getLevel_info().getCurrent_level() + 1) + "还需要: " + 37 | (needExp / todayExp) + "天"); 38 | } else { 39 | log.info("当前等级Lv6,经验值为:" + userInfo.getLevel_info().getCurrent_exp()); 40 | } 41 | } 42 | 43 | /** 44 | * 获取当前投币获得的经验值 45 | * 46 | * @return 本日已经投了几个币 47 | */ 48 | public static int expConfirm() { 49 | JsonObject resultJson = HttpUtil.doGet(ApiList.needCoinNew); 50 | int getCoinExp = resultJson.get("data").getAsInt(); 51 | return getCoinExp / 10; 52 | } 53 | 54 | 55 | /** 56 | * 此功能依赖UserCheck 57 | * 58 | * @return 返回会员类型 59 | * 0:无会员(会员过期,当前不是会员) 60 | * 1:月会员 61 | * 2:年会员 62 | */ 63 | public static int queryVipStatusType() { 64 | if (userInfo == null) { 65 | log.info("暂时无法查询会员状态,默认非大会员"); 66 | } 67 | if (userInfo != null && userInfo.getVipStatus() == 1) { 68 | //只有VipStatus为1的时候获取到VipType才是有效的。 69 | return userInfo.getVipType(); 70 | } else { 71 | return 0; 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/ServerPush.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import top.misec.login.ServerVerify; 5 | import top.misec.push.PushHelper; 6 | import top.misec.push.impl.PushPlusPush; 7 | import top.misec.push.model.PushMetaInfo; 8 | import top.misec.utils.LoadFileResource; 9 | 10 | /** 11 | * @author @JunzhouLiu @Kurenai 12 | * @create 2020/10/21 17:39 13 | */ 14 | 15 | @Slf4j 16 | public class ServerPush { 17 | 18 | public static void doServerPush() { 19 | PushMetaInfo.PushMetaInfoBuilder builder = PushMetaInfo.builder().numberOfRetries(3); 20 | PushHelper.Target target = null; 21 | if (ServerVerify.getFtkey() != null && ServerVerify.getChatId() == null) { 22 | builder = builder.token(ServerVerify.getFtkey()); 23 | // 临时解决方案 24 | if (ServerVerify.getFtkey().startsWith("https://oapi.dingtalk.com")) { 25 | target = PushHelper.Target.DING_TALK; 26 | log.info("本次执行推送日志到钉钉"); 27 | } else if (ServerVerify.getFtkey().startsWith("SCU")) { 28 | target = PushHelper.Target.SERVER_CHAN; 29 | log.info("本次执行推送日志到Server酱"); 30 | log.info("Server酱旧版推送渠道即将下线,请前往[sct.ftqq.com](http://sct.ftqq.com/)使用Turbo版本的推送Key"); 31 | } else if (ServerVerify.getFtkey().startsWith("SCT")) { 32 | target = PushHelper.Target.SERVER_CHAN_TURBO; 33 | log.info("本次执行推送日志到Server酱Turbo版本"); 34 | } else if (ServerVerify.getFtkey().length() == PushPlusPush.PUSH_PLUS_CHANNEL_TOKEN_DEFAULT_LENGTH) { 35 | target = PushHelper.Target.PUSH_PLUS; 36 | log.info("本次执行推送日志到Push Plus"); 37 | } 38 | 39 | } else if (ServerVerify.getFtkey() != null) { 40 | builder = builder.token(ServerVerify.getFtkey()).chatId(ServerVerify.getChatId()); 41 | target = PushHelper.Target.TELEGRAM; 42 | log.info("本次执行推送日志到Telegram"); 43 | } else { 44 | log.info("未配置server酱,本次执行不推送日志到微信"); 45 | log.info("未配置Telegram,本次执行不推送日志到Telegram"); 46 | } 47 | if (null != target) { 48 | PushHelper.push(target, builder.build(), LoadFileResource.loadFile("/tmp/daily.log")); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/top/misec/utils/LoadFileResource.java: -------------------------------------------------------------------------------- 1 | package top.misec.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.*; 6 | import java.nio.charset.StandardCharsets; 7 | 8 | /** 9 | * @author Junzhou Liu 10 | * @create 2020/10/17 19:31 11 | * 工具类通过流的方式读取文件 12 | */ 13 | @Slf4j 14 | public class LoadFileResource { 15 | 16 | /** 17 | * 从外部资源读取配置文件 18 | * 19 | * @return config 20 | */ 21 | public static String loadConfigJsonFromFile() { 22 | String config = null; 23 | try { 24 | String outPath = System.getProperty("user.dir") + File.separator + "config.json" + File.separator; 25 | InputStream is = new FileInputStream(outPath); 26 | int size = is.available(); 27 | byte[] buffer = new byte[size]; 28 | is.read(buffer); 29 | is.close(); 30 | config = new String(buffer, StandardCharsets.UTF_8); 31 | } catch (FileNotFoundException e) { 32 | log.info("未扫描到外部配置文件,即将加载默认配置文件【此提示仅针自行部署的Linux用户,普通用户请忽略】"); 33 | } catch (IOException e) { 34 | e.printStackTrace(); 35 | log.debug("", e); 36 | } 37 | return config; 38 | } 39 | 40 | 41 | /** 42 | * 从resource读取版本文件 43 | * 44 | * @param fileName 文件名 45 | * @return 返回读取到文件 46 | */ 47 | public static String loadJsonFromAsset(String fileName) { 48 | String json = null; 49 | try { 50 | InputStream is = LoadFileResource.class.getClassLoader().getResourceAsStream(fileName); 51 | assert is != null; 52 | int size = is.available(); 53 | byte[] buffer = new byte[size]; 54 | is.read(buffer); 55 | is.close(); 56 | json = new String(buffer, StandardCharsets.UTF_8); 57 | 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | log.debug("", e); 61 | } 62 | return json; 63 | } 64 | 65 | 66 | /** 67 | * @param filePath 读入的文件路径 68 | * @return 返回str 69 | */ 70 | public static String loadFile(String filePath) { 71 | String logs = null; 72 | try { 73 | InputStream is = new FileInputStream(filePath); 74 | int size = is.available(); 75 | byte[] buffer = new byte[size]; 76 | is.read(buffer); 77 | is.close(); 78 | logs = new String(buffer, StandardCharsets.UTF_8); 79 | } catch (IOException e) { 80 | e.printStackTrace(); 81 | log.debug("", e); 82 | } 83 | return logs; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/top/misec/push/AbstractPush.java: -------------------------------------------------------------------------------- 1 | package top.misec.push; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.push.model.PushMetaInfo; 6 | import top.misec.push.model.PushResult; 7 | import top.misec.push.model.RetryContext; 8 | import top.misec.utils.HttpUtil; 9 | 10 | /** 11 | * 推送抽象类;公共模板方法封装 12 | * 13 | * @author itning 14 | * @since 2021/3/22 16:36 15 | */ 16 | @Slf4j 17 | public abstract class AbstractPush implements Push { 18 | 19 | @Override 20 | public final PushResult doPush(PushMetaInfo metaInfo, String content) { 21 | String url = generatePushUrl(metaInfo); 22 | assert null != url : "推送URL不能为空"; 23 | String pushContent = generatePushBody(metaInfo, content); 24 | JsonObject jsonObject = HttpUtil.doPost(url, pushContent); 25 | boolean pushStatus = checkPushStatus(jsonObject); 26 | if (pushStatus) { 27 | log.info("任务状态推送成功"); 28 | return PushResult.success(); 29 | } else { 30 | log.info("任务状态推送失败,开始重试"); 31 | return retryPush(new RetryContext(url, pushContent, metaInfo.getNumberOfRetries(), metaInfo.getRetryInterval())); 32 | } 33 | } 34 | 35 | /** 36 | * 重试推送 37 | * 38 | * @param context 重试上下文 39 | */ 40 | private PushResult retryPush(RetryContext context) { 41 | while (context.next()) { 42 | JsonObject jsonObject = HttpUtil.doPost(context.getUrl(), context.getBody()); 43 | boolean pushStatus = checkPushStatus(jsonObject); 44 | if (pushStatus) { 45 | log.info("任务状态推送成功"); 46 | return PushResult.success(); 47 | } else { 48 | log.info("任务状态推送失败,开始第{}次重试", context.getRetryCount()); 49 | log.debug("{}", jsonObject); 50 | } 51 | } 52 | return PushResult.failed(); 53 | } 54 | 55 | /** 56 | * 生成推送URL 57 | * 58 | * @param metaInfo 元信息 59 | * @return URL字符串 60 | */ 61 | protected abstract String generatePushUrl(final PushMetaInfo metaInfo); 62 | 63 | /** 64 | * 检查推送结果 65 | * 66 | * @param jsonObject HTTP结果,可能为null 67 | * @return 推送成功,返回true 68 | */ 69 | protected abstract boolean checkPushStatus(final JsonObject jsonObject); 70 | 71 | /** 72 | * 生成要推送的内容信息 73 | * 74 | * @param metaInfo 元信息 75 | * @param content 要推送的内容 76 | * @return 整理后的推送内容 77 | */ 78 | protected String generatePushBody(final PushMetaInfo metaInfo, final String content) { 79 | return content; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/VideoWatch.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.apiquery.oftenAPI; 7 | import top.misec.login.Verify; 8 | import top.misec.utils.HttpUtil; 9 | 10 | import java.util.Random; 11 | 12 | import static top.misec.task.DailyTask.getDailyTaskStatus; 13 | import static top.misec.task.TaskInfoHolder.STATUS_CODE_STR; 14 | import static top.misec.task.TaskInfoHolder.getVideoId; 15 | 16 | /** 17 | * 观看分享视频 18 | * 19 | * @author @JunzhouLiu @Kurenai 20 | * @since 2020-11-22 5:13 21 | */ 22 | @Slf4j 23 | public class VideoWatch implements Task { 24 | 25 | private final String taskName = "观看分享视频"; 26 | 27 | @Override 28 | public void run() { 29 | 30 | JsonObject dailyTaskStatus = getDailyTaskStatus(); 31 | String bvid = getVideoId.getRegionRankingVideoBvid(); 32 | if (!dailyTaskStatus.get("watch").getAsBoolean()) { 33 | int playedTime = new Random().nextInt(90) + 1; 34 | String postBody = "bvid=" + bvid 35 | + "&played_time=" + playedTime; 36 | JsonObject resultJson = HttpUtil.doPost(ApiList.videoHeartbeat, postBody); 37 | String videoTitle = oftenAPI.videoTitle(bvid); 38 | int responseCode = resultJson.get(STATUS_CODE_STR).getAsInt(); 39 | if (responseCode == 0) { 40 | log.info("视频: " + videoTitle + "播放成功,已观看到第" + playedTime + "秒"); 41 | } else { 42 | log.debug("视频: " + videoTitle + "播放失败,原因: " + resultJson.get("message").getAsString()); 43 | } 44 | } else { 45 | log.info("本日观看视频任务已经完成了,不需要再观看视频了"); 46 | } 47 | 48 | if (!dailyTaskStatus.get("share").getAsBoolean()) { 49 | dailyAvShare(bvid); 50 | } else { 51 | log.info("本日分享视频任务已经完成了,不需要再分享视频了"); 52 | } 53 | } 54 | 55 | @Override 56 | public String getName() { 57 | return taskName; 58 | } 59 | 60 | /** 61 | * @param bvid 要分享的视频bvid 62 | */ 63 | private void dailyAvShare(String bvid) { 64 | String requestBody = "bvid=" + bvid + "&csrf=" + Verify.getInstance().getBiliJct(); 65 | JsonObject result = HttpUtil.doPost((ApiList.AvShare), requestBody); 66 | 67 | String videoTitle = oftenAPI.videoTitle(bvid); 68 | 69 | if (result.get(STATUS_CODE_STR).getAsInt() == 0) { 70 | log.info("视频: " + videoTitle + " 分享成功"); 71 | } else { 72 | log.debug("视频分享失败,原因: " + result.get("message").getAsString()); 73 | log.debug("开发者提示: 如果是csrf校验失败请检查BILI_JCT参数是否正确或者失效"); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/GetVipPrivilege.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.Data; 5 | import lombok.extern.slf4j.Slf4j; 6 | import top.misec.apiquery.ApiList; 7 | import top.misec.apiquery.oftenAPI; 8 | import top.misec.utils.HttpUtil; 9 | 10 | import java.util.Calendar; 11 | import java.util.TimeZone; 12 | 13 | import static top.misec.task.TaskInfoHolder.STATUS_CODE_STR; 14 | import static top.misec.task.TaskInfoHolder.queryVipStatusType; 15 | 16 | /** 17 | * 漫画权益领取 18 | * 19 | * @author @JunzhouLiu @Kurenai @happy888888 20 | * @since 2020-11-22 5:48 21 | */ 22 | @Slf4j 23 | @Data 24 | public class GetVipPrivilege implements Task { 25 | 26 | /** 27 | * 权益号,由https://api.bilibili.com/x/vip/privilege/my 28 | * 得到权益号数组,取值范围为数组中的整数 29 | * 为方便直接取1,为领取漫读劵,暂时不取其他的值 30 | */ 31 | private int reasonId = 1; 32 | 33 | public GetVipPrivilege() { 34 | } 35 | 36 | /** 37 | * @param reasonId 1 38 | */ 39 | @ExtensionMethod 40 | public GetVipPrivilege(int reasonId) { 41 | this.reasonId = reasonId; 42 | } 43 | 44 | @Override 45 | public void run() { 46 | Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+8")); 47 | int day = cal.get(Calendar.DATE); 48 | 49 | //根据userInfo.getVipStatus() ,如果是1 ,会员有效,0会员失效。 50 | //@JunzhouLiu: fixed query_vipStatusType()现在可以查询会员状态,以及会员类型了 2020-10-15 51 | int vipType = queryVipStatusType(); 52 | 53 | if (vipType == 0) { 54 | log.info("非大会员,跳过领取大会员权益"); 55 | return; 56 | } 57 | 58 | if (vipType == 1 && day == 1) { 59 | log.info("开始领取大会员漫画权益"); 60 | String requestBody = "{\"reason_id\":" + reasonId + "}"; 61 | //注意参数构造格式为json,不知道需不需要重载下面的Post函数改请求头 62 | JsonObject jsonObject = HttpUtil.doPost(ApiList.mangaGetVipReward, requestBody); 63 | if (jsonObject.get(STATUS_CODE_STR).getAsInt() == 0) { 64 | //@happy888888:好像也可以getAsString或,getAsShort 65 | //@JunzhouLiu:Int比较好判断 66 | log.info("大会员成功领取" + jsonObject.get("data").getAsJsonObject().get("amount").getAsInt() + "张漫读劵"); 67 | } else { 68 | log.info("大会员领取漫读劵失败,原因为:" + jsonObject.get("msg").getAsString()); 69 | } 70 | } else { 71 | log.info("本日非领取大会员漫画执行日期"); 72 | } 73 | 74 | if (day == 1 || day % 7 == 0 && vipType == 2) { 75 | log.info("开始领取年度大会员权益"); 76 | oftenAPI.vipPrivilege(1); 77 | oftenAPI.vipPrivilege(2); 78 | } else { 79 | log.info("本日非领取年度大会员权益执行日期"); 80 | } 81 | 82 | } 83 | 84 | @Override 85 | public String getName() { 86 | return "漫画权益领取"; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/DailyTask.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.utils.HttpUtil; 7 | 8 | import java.text.SimpleDateFormat; 9 | import java.util.*; 10 | 11 | import static top.misec.task.TaskInfoHolder.STATUS_CODE_STR; 12 | import static top.misec.task.TaskInfoHolder.calculateUpgradeDays; 13 | 14 | /** 15 | * @author @JunzhouLiu @Kurenai 16 | * @create 2020/10/11 20:44 17 | */ 18 | @Slf4j 19 | public class DailyTask { 20 | 21 | private final List dailyTasks; 22 | 23 | public DailyTask() { 24 | dailyTasks = new ArrayList<>(); 25 | dailyTasks.add(new VideoWatch()); 26 | dailyTasks.add(new MangaSign()); 27 | dailyTasks.add(new CoinAdd()); 28 | dailyTasks.add(new Silver2coin()); 29 | dailyTasks.add(new LiveCheckin()); 30 | dailyTasks.add(new GiveGift()); 31 | dailyTasks.add(new ChargeMe()); 32 | dailyTasks.add(new GetVipPrivilege()); 33 | Collections.shuffle(dailyTasks); 34 | dailyTasks.add(0, new UserCheck()); 35 | } 36 | 37 | /** 38 | * @return jsonObject 返回status对象,包含{"login":true,"watch":true,"coins":50, 39 | * "share":true,"email":true,"tel":true,"safe_question":true,"identify_card":false} 40 | * @author @srcrs 41 | */ 42 | public static JsonObject getDailyTaskStatus() { 43 | JsonObject jsonObject = HttpUtil.doGet(ApiList.reward); 44 | int responseCode = jsonObject.get(STATUS_CODE_STR).getAsInt(); 45 | if (responseCode == 0) { 46 | log.info("请求本日任务完成状态成功"); 47 | return jsonObject.get("data").getAsJsonObject(); 48 | } else { 49 | log.debug(jsonObject.get("message").getAsString()); 50 | return HttpUtil.doGet(ApiList.reward).get("data").getAsJsonObject(); 51 | //偶发性请求失败,再请求一次。 52 | } 53 | } 54 | 55 | public void doDailyTask() { 56 | try { 57 | printTime(); 58 | log.debug("任务启动中"); 59 | for (Task task : dailyTasks) { 60 | log.info("------{}开始------", task.getName()); 61 | task.run(); 62 | log.info("------{}结束------\n", task.getName()); 63 | taskSuspend(); 64 | } 65 | log.info("本日任务已全部执行完毕"); 66 | calculateUpgradeDays(); 67 | } catch (InterruptedException e) { 68 | e.printStackTrace(); 69 | } finally { 70 | ServerPush.doServerPush(); 71 | } 72 | } 73 | 74 | private void printTime() { 75 | Date d = new Date(); 76 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 77 | String time = sdf.format(d); 78 | log.info(time); 79 | } 80 | 81 | private void taskSuspend() throws InterruptedException { 82 | Random random = new Random(); 83 | int sleepTime = (int) ((random.nextDouble() + 0.5) * 3000); 84 | log.info("-----随机暂停{}ms-----\n", sleepTime); 85 | Thread.sleep(sleepTime); 86 | } 87 | 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/main/java/top/misec/apiquery/oftenAPI.java: -------------------------------------------------------------------------------- 1 | package top.misec.apiquery; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.login.Verify; 6 | import top.misec.utils.HttpUtil; 7 | 8 | /** 9 | * 部分API简单封装。 10 | * 11 | * @author Junzhou Liu 12 | * @create 2020/10/14 14:27 13 | */ 14 | @Slf4j 15 | public class oftenAPI { 16 | 17 | /** 18 | * @return 返回主站查询到的硬币余额,查询失败返回0.0 19 | */ 20 | public static Double getCoinBalance() { 21 | JsonObject responseJson = HttpUtil.doGet(ApiList.getCoinBalance); 22 | int responseCode = responseJson.get("code").getAsInt(); 23 | 24 | JsonObject dataObject = responseJson.get("data").getAsJsonObject(); 25 | 26 | if (responseCode == 0) { 27 | if (dataObject.get("money").isJsonNull()) { 28 | return 0.0; 29 | } else { 30 | return dataObject.get("money").getAsDouble(); 31 | } 32 | } else { 33 | log.debug("请求硬币余额接口错误,请稍后重试。错误请求信息:" + responseJson); 34 | return 0.0; 35 | } 36 | } 37 | 38 | /** 39 | * @param type 1大会员B币券 2 大会员福利 40 | */ 41 | public static void vipPrivilege(int type) { 42 | String requestBody = "type=" + type 43 | + "&csrf=" + Verify.getInstance().getBiliJct(); 44 | JsonObject jsonObject = HttpUtil.doPost(ApiList.vipPrivilegeReceive, requestBody); 45 | int responseCode = jsonObject.get("code").getAsInt(); 46 | if (responseCode == 0) { 47 | if (type == 1) { 48 | log.info("领取年度大会员每月赠送的B币券成功"); 49 | } else if (type == 2) { 50 | log.info("领取大会员福利/权益成功"); 51 | } 52 | 53 | } else { 54 | log.debug("领取年度大会员每月赠送的B币券/大会员福利失败,原因: " + jsonObject.get("message").getAsString()); 55 | } 56 | } 57 | 58 | /** 59 | * 请求视频title,未获取到时返回bvid 60 | * 61 | * @return title 62 | */ 63 | public static String videoTitle(String bvid) { 64 | String title; 65 | String urlParameter = "?bvid=" + bvid; 66 | JsonObject jsonObject = HttpUtil.doGet(ApiList.videoView + urlParameter); 67 | 68 | if (jsonObject.get("code").getAsInt() == 0) { 69 | title = jsonObject.getAsJsonObject("data").getAsJsonObject("owner").get("name").getAsString() + ": "; 70 | title += jsonObject.getAsJsonObject("data").get("title").getAsString(); 71 | } else { 72 | title = "未能获取标题"; 73 | log.info(title); 74 | log.debug(jsonObject.get("message").getAsString()); 75 | } 76 | 77 | return title.replace("&", "-"); 78 | } 79 | 80 | /** 81 | * @param uid 用户uid 82 | * @return userName 查询到的用户名,为1则未查询到用户 83 | */ 84 | public static String queryUserName(String uid) { 85 | String urlParameter = "?mid=" + uid + "&jsonp=jsonp"; 86 | String userName = "1"; 87 | JsonObject jsonObject = HttpUtil.doGet(ApiList.queryUserName + urlParameter); 88 | if (jsonObject.get("code").getAsInt() == 0) { 89 | userName = jsonObject.getAsJsonObject("data").get("name").getAsString(); 90 | } else { 91 | log.info("查询充电对象的用户名失败,原因:{}", jsonObject); 92 | } 93 | return userName; 94 | } 95 | 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/top/misec/apiquery/ApiList.java: -------------------------------------------------------------------------------- 1 | package top.misec.apiquery; 2 | 3 | /** 4 | * @author Junzhou Liu 5 | * @create 2020/10/11 3:40 6 | */ 7 | public class ApiList { 8 | 9 | public static String PushPlus = "http://www.pushplus.plus/send"; 10 | public static String ServerPush = "https://sc.ftqq.com/"; 11 | public static String ServerPushV2 = "https://sctapi.ftqq.com/"; 12 | public static String ServerPushTelegram = "https://api.telegram.org/bot"; 13 | public static String LOGIN = "https://api.bilibili.com/x/web-interface/nav"; 14 | public static String Manga = "https://manga.bilibili.com/twirp/activity.v1.Activity/ClockIn"; 15 | public static String AvShare = "https://api.bilibili.com/x/web-interface/share/add"; 16 | public static String CoinAdd = "https://api.bilibili.com/x/web-interface/coin/add"; 17 | public static String isCoin = "https://api.bilibili.com/x/web-interface/archive/coins"; 18 | public static String getRegionRanking = "https://api.bilibili.com/x/web-interface/ranking/region"; 19 | public static String reward = "https://api.bilibili.com/x/member/web/exp/reward"; 20 | 21 | /** 22 | * 查询获取已获取的投币经验 23 | */ 24 | public static String needCoin = "https://www.bilibili.com/plus/account/exp.php"; 25 | 26 | public static String needCoinNew = "https://api.bilibili.com/x/web-interface/coin/today/exp"; 27 | 28 | /** 29 | * 硬币换银瓜子 30 | */ 31 | public static String silver2coin = "https://api.live.bilibili.com/pay/v1/Exchange/silver2coin"; 32 | 33 | /** 34 | * 查询银瓜子兑换状态 35 | */ 36 | public static String getSilver2coinStatus = "https://api.live.bilibili.com/pay/v1/Exchange/getStatus"; 37 | 38 | /** 39 | * 上报观看进度 40 | */ 41 | public static String videoHeartbeat = "https://api.bilibili.com/x/click-interface/web/heartbeat"; 42 | 43 | /** 44 | * 查询主站硬币余额 45 | */ 46 | public static String getCoinBalance = "https://account.bilibili.com/site/getCoin"; 47 | 48 | /** 49 | * 充电请求 50 | */ 51 | public static String autoCharge = "https://api.bilibili.com/x/ugcpay/web/v2/trade/elec/pay/quick"; 52 | 53 | /** 54 | * 充电留言 55 | */ 56 | public static String chargeComment = "https://api.bilibili.com/x/ugcpay/trade/elec/message"; 57 | 58 | 59 | public static String chargeQuery = "https://api.bilibili.com/x/ugcpay/web/v2/trade/elec/panel"; 60 | 61 | public static String queryUserName = "https://api.bilibili.com/x/space/acc/info"; 62 | 63 | /** 64 | * 领取大会员福利 65 | */ 66 | public static String vipPrivilegeReceive = "https://api.bilibili.com/x/vip/privilege/receive"; 67 | 68 | /** 69 | * 领取大会员漫画福利 70 | */ 71 | public static String mangaGetVipReward = "https://manga.bilibili.com/twirp/user.v1.User/GetVipReward"; 72 | /** 73 | * 直播签到 74 | */ 75 | public static String liveCheckin = "https://api.live.bilibili.com/xlive/web-ucenter/v1/sign/DoSign"; 76 | 77 | public static String queryDynamicNew = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new"; 78 | 79 | public static String videoView = "https://api.bilibili.com/x/web-interface/view"; 80 | 81 | /** 82 | * 83 | */ 84 | public static String mangaRead = "https://manga.bilibili.com/twirp/bookshelf.v1.Bookshelf/AddHistory"; 85 | /** 86 | * 87 | */ 88 | public static String getBvidByCreate = "https://api.bilibili.com/x/space/arc/search"; 89 | } 90 | -------------------------------------------------------------------------------- /.github/workflows/auto_package_bilibili-helper.yml: -------------------------------------------------------------------------------- 1 | name: Auto_Package_BILIBILI-HELPER 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - "src/main/resources/release.json" 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up JDK 1.8 17 | uses: actions/setup-java@v1 18 | with: 19 | java-version: 1.8 20 | # 从release.json 读取相关信息 21 | - name: Read tag_main 22 | id: tag_main 23 | uses: ashley-taylor/read-json-property-action@v1.0 24 | with: 25 | path: ./src/main/resources/release.json 26 | property: tag_main 27 | 28 | - name: Read tag_latest 29 | id: tag_latest 30 | uses: ashley-taylor/read-json-property-action@v1.0 31 | with: 32 | path: ./src/main/resources/release.json 33 | property: tag_latest 34 | 35 | - name: Read description 36 | id: description 37 | uses: juliangruber/read-file-action@v1 38 | with: 39 | path: ./src/main/resources/release.info 40 | 41 | - name: Cache local Maven repository 42 | uses: actions/cache@v2 43 | with: 44 | path: ~/.m2/repository 45 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 46 | restore-keys: | 47 | ${{ runner.os }}-maven- 48 | 49 | - name: Package with Maven 50 | run: mvn -B package assembly:single --file pom.xml -Dmaven.test.skip=true 51 | 52 | - name: ZIP files 53 | run: | 54 | mv -f ./target/BILIBILI-HELPER-${{steps.tag_main.outputs.value}}-jar-with-dependencies.jar BILIBILI-HELPER-v${{steps.tag_latest.outputs.value}}.jar 55 | zip BILIBILI-HELPER\ v${{steps.tag_latest.outputs.value}}.zip BILIBILI-HELPER-v${{steps.tag_latest.outputs.value}}.jar 56 | cp ./src/main/resources/config.json ./ 57 | zip BILIBILI-HELPER\ v${{steps.tag_latest.outputs.value}}.zip config.json 58 | zip BILIBILI-HELPER\ v${{steps.tag_latest.outputs.value}}.zip LICENSE 59 | zip BILIBILI-HELPER\ v${{steps.tag_latest.outputs.value}}.zip README.md 60 | 61 | - name: Create Release 62 | id: create_release 63 | uses: actions/create-release@latest 64 | env: 65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token 66 | with: 67 | tag_name: V${{steps.tag_latest.outputs.value}} 68 | release_name: BILIBILI-HELPER-v${{steps.tag_latest.outputs.value}} 69 | body: | 70 | ${{steps.description.outputs.content}} 71 | draft: false 72 | prerelease: false 73 | 74 | - name: Upload Release Asset 75 | id: upload-release-asset 76 | uses: actions/upload-release-asset@v1 77 | env: 78 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 79 | with: 80 | upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps 81 | asset_path: ./BILIBILI-HELPER v${{steps.tag_latest.outputs.value}}.zip 82 | asset_name: BILIBILI-HELPER-v${{steps.tag_latest.outputs.value}}.zip 83 | asset_content_type: application/zip -------------------------------------------------------------------------------- /src/main/java/org/slf4j/helpers/Util.java: -------------------------------------------------------------------------------- 1 | package org.slf4j.helpers; 2 | 3 | /** 4 | * @author itning 5 | * @since 2021/5/2 19:11 6 | */ 7 | @SuppressWarnings("all") 8 | public class Util { 9 | private Util() { 10 | } 11 | 12 | public static String safeGetSystemProperty(String key) { 13 | if (key == null) 14 | throw new IllegalArgumentException("null input"); 15 | 16 | String result = null; 17 | try { 18 | result = System.getProperty(key); 19 | } catch (java.lang.SecurityException sm) { 20 | ; // ignore 21 | } 22 | return result; 23 | } 24 | 25 | public static boolean safeGetBooleanSystemProperty(String key) { 26 | String value = safeGetSystemProperty(key); 27 | if (value == null) 28 | return false; 29 | else 30 | return value.equalsIgnoreCase("true"); 31 | } 32 | 33 | /** 34 | * In order to call {@link SecurityManager#getClassContext()}, which is a 35 | * protected method, we add this wrapper which allows the method to be visible 36 | * inside this package. 37 | */ 38 | private static final class ClassContextSecurityManager extends SecurityManager { 39 | protected Class[] getClassContext() { 40 | return super.getClassContext(); 41 | } 42 | } 43 | 44 | private static ClassContextSecurityManager SECURITY_MANAGER; 45 | private static boolean SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED = false; 46 | 47 | private static ClassContextSecurityManager getSecurityManager() { 48 | if (SECURITY_MANAGER != null) 49 | return SECURITY_MANAGER; 50 | else if (SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED) 51 | return null; 52 | else { 53 | SECURITY_MANAGER = safeCreateSecurityManager(); 54 | SECURITY_MANAGER_CREATION_ALREADY_ATTEMPTED = true; 55 | return SECURITY_MANAGER; 56 | } 57 | } 58 | 59 | private static ClassContextSecurityManager safeCreateSecurityManager() { 60 | try { 61 | return new ClassContextSecurityManager(); 62 | } catch (java.lang.SecurityException sm) { 63 | return null; 64 | } 65 | } 66 | 67 | /** 68 | * Returns the name of the class which called the invoking method. 69 | * 70 | * @return the name of the class which called the invoking method. 71 | */ 72 | public static Class getCallingClass() { 73 | ClassContextSecurityManager securityManager = getSecurityManager(); 74 | if (securityManager == null) 75 | return null; 76 | Class[] trace = securityManager.getClassContext(); 77 | String thisClassName = Util.class.getName(); 78 | 79 | // Advance until Util is found 80 | int i; 81 | for (i = 0; i < trace.length; i++) { 82 | if (thisClassName.equals(trace[i].getName())) 83 | break; 84 | } 85 | 86 | // trace[i] = Util; trace[i+1] = caller; trace[i+2] = caller's caller 87 | if (i >= trace.length || i + 2 >= trace.length) { 88 | throw new IllegalStateException("Failed to find org.slf4j.helpers.Util or its caller in the stack; " + "this should not happen"); 89 | } 90 | 91 | return trace[i + 2]; 92 | } 93 | 94 | static final public void report(String msg, Throwable t) { 95 | System.err.println(msg); 96 | System.err.println("Reported exception:"); 97 | t.printStackTrace(); 98 | } 99 | 100 | static final public void report(String msg) { 101 | // System.err.println("SLF4J: " + msg); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/top/misec/config/Config.java: -------------------------------------------------------------------------------- 1 | package top.misec.config; 2 | 3 | import com.google.gson.Gson; 4 | import lombok.Data; 5 | import lombok.extern.slf4j.Slf4j; 6 | import top.misec.utils.HttpUtil; 7 | import top.misec.utils.LoadFileResource; 8 | 9 | /** 10 | * Auto-generated: 2020-10-13 17:10:40 11 | * 12 | * @author Junzhou Liu 13 | * @create 2020/10/13 17:11 14 | */ 15 | @Slf4j 16 | @Data 17 | public class Config { 18 | 19 | private static Config CONFIG = new Config(); 20 | /** 21 | * 每日设定的投币数 [0,5] 22 | */ 23 | private int numberOfCoins; 24 | /** 25 | * 投币时是否点赞 [0,1] 26 | */ 27 | private int selectLike; 28 | /** 29 | * 年度大会员自动充电[false,true] 30 | */ 31 | private boolean monthEndAutoCharge; 32 | /** 33 | * 自动打赏快过期礼物[false,true] 34 | */ 35 | private boolean giveGift; 36 | /** 37 | * 打赏快过期礼物对象,为http://live.bilibili.com/后的数字 38 | * 填0表示随机打赏。 39 | */ 40 | private String upLive; 41 | /** 42 | * 执行客户端操作时的平台 [ios,android] 43 | */ 44 | private String devicePlatform; 45 | /** 46 | * 投币优先级 [0,1] 47 | * 0:优先给热榜视频投币,1:优先给关注的up投币 48 | */ 49 | private int coinAddPriority; 50 | private String userAgent; 51 | private boolean skipDailyTask; 52 | private String chargeForLove; 53 | private int reserveCoins; 54 | 55 | private Config() { 56 | } 57 | 58 | public static Config getInstance() { 59 | return CONFIG; 60 | } 61 | 62 | 63 | @Override 64 | public String toString() { 65 | return "配置信息{" + 66 | "每日投币数为:" + numberOfCoins + 67 | "分享时是否点赞:" + selectLike + 68 | "月底是否充电:" + monthEndAutoCharge + 69 | "执行app客户端操作的系统是:" + devicePlatform + 70 | "投币策略:" + coinAddPriority + "\n" + 71 | "UA是:" + userAgent + "\n" + 72 | "是否跳过每日任务:" + skipDailyTask + 73 | '}'; 74 | } 75 | 76 | public void configInit(String json) { 77 | Config.CONFIG = new Gson().fromJson(json, Config.class); 78 | HttpUtil.setUserAgent(Config.getInstance().getUserAgent()); 79 | log.info(Config.getInstance().toString()); 80 | } 81 | 82 | 83 | /** 84 | * 优先从jar包同级目录读取 85 | * 读取配置文件 src/main/resources/config.json 86 | */ 87 | public void configInit() { 88 | String configJson; 89 | String outConfig = LoadFileResource.loadConfigJsonFromFile(); 90 | if (outConfig != null) { 91 | configJson = outConfig; 92 | log.info("读取外部配置文件成功"); 93 | } else { 94 | String temp = LoadFileResource.loadJsonFromAsset("config.json"); 95 | /** 96 | *兼容旧配置文件 97 | * "skipDailyTask": 0 -> "skipDailyTask": false 98 | * "skipDailyTask": 1 -> "skipDailyTask": true 99 | */ 100 | String target0 = "\"skipDailyTask\": 0"; 101 | String target1 = "\"skipDailyTask\": 1"; 102 | if (temp.contains(target0)) { 103 | log.debug("兼容旧配置文件,skipDailyTask的值由0变更为false"); 104 | configJson = temp.replaceAll(target0, "\"skipDailyTask\": false"); 105 | } else if (temp.contains(target1)) { 106 | log.debug("兼容旧配置文件,skipDailyTask的值由1变更为true"); 107 | configJson = temp.replaceAll(target1, "\"skipDailyTask\": true"); 108 | } else { 109 | log.debug("使用的是最新格式的配置文件,无需执行兼容性转换"); 110 | configJson = temp; 111 | } 112 | 113 | log.info("读取配置文件成功"); 114 | } 115 | 116 | Config.CONFIG = new Gson().fromJson(configJson, Config.class); 117 | HttpUtil.setUserAgent(Config.getInstance().getUserAgent()); 118 | log.info(Config.getInstance().toString()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/top/misec/BiliMain.java: -------------------------------------------------------------------------------- 1 | package top.misec; 2 | 3 | import com.google.gson.Gson; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.slf4j.impl.StaticLoggerBinder; 7 | import top.misec.config.Config; 8 | import top.misec.login.ServerVerify; 9 | import top.misec.login.Verify; 10 | import top.misec.task.DailyTask; 11 | import top.misec.task.ServerPush; 12 | import top.misec.utils.VersionInfo; 13 | 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.util.logging.LogManager; 17 | 18 | 19 | /** 20 | * @author Junzhou Liu 21 | * @create 2020/10/11 2:29 22 | */ 23 | 24 | public class BiliMain { 25 | private static final Logger log; 26 | 27 | static { 28 | // 如果此标记为true,则为腾讯云函数,使用JUL作为日志输出。 29 | boolean scfFlag = Boolean.getBoolean("scfFlag"); 30 | StaticLoggerBinder.LOG_IMPL = scfFlag ? StaticLoggerBinder.LogImpl.JUL : StaticLoggerBinder.LogImpl.LOG4J2; 31 | log = LoggerFactory.getLogger(BiliMain.class); 32 | final InputStream inputStream = BiliMain.class.getResourceAsStream("/logging.properties"); 33 | try { 34 | LogManager.getLogManager().readConfiguration(inputStream); 35 | } catch (final IOException e) { 36 | java.util.logging.Logger.getAnonymousLogger().severe("Could not load default logging.properties file"); 37 | java.util.logging.Logger.getAnonymousLogger().severe(e.getMessage()); 38 | } 39 | } 40 | 41 | public static void main(String[] args) { 42 | 43 | if (args.length < 3) { 44 | log.info("任务启动失败"); 45 | log.warn("Cookies参数缺失,请检查是否在Github Secrets中配置Cookies参数"); 46 | return; 47 | } 48 | //读取环境变量 49 | Verify.verifyInit(args[0], args[1], args[2]); 50 | 51 | if (args.length > 4) { 52 | ServerVerify.verifyInit(args[3], args[4]); 53 | } else if (args.length > 3) { 54 | ServerVerify.verifyInit(args[3]); 55 | } 56 | 57 | 58 | VersionInfo.printVersionInfo(); 59 | //每日任务65经验 60 | Config.getInstance().configInit(); 61 | if (!Config.getInstance().isSkipDailyTask()) { 62 | DailyTask dailyTask = new DailyTask(); 63 | dailyTask.doDailyTask(); 64 | } else { 65 | log.info("已开启了跳过本日任务,本日任务跳过(不会发起任何网络请求),如果需要取消跳过,请将skipDailyTask值改为false"); 66 | ServerPush.doServerPush(); 67 | } 68 | } 69 | 70 | /** 71 | * 用于腾讯云函数触发 72 | */ 73 | public static void mainHandler(KeyValueClass ignored) { 74 | StaticLoggerBinder.LOG_IMPL = StaticLoggerBinder.LogImpl.JUL; 75 | String config = System.getProperty("config"); 76 | if (null == config) { 77 | System.out.println("取config配置为空!!!"); 78 | return; 79 | } 80 | KeyValueClass kv = new Gson().fromJson(config, KeyValueClass.class); 81 | System.out.println("环境信息:"); 82 | System.out.println(kv); 83 | //读取环境变量 84 | Verify.verifyInit(kv.getDedeuserid(), kv.getSessdata(), kv.getBiliJct()); 85 | 86 | if (null != kv.getTelegrambottoken() && null != kv.getTelegramchatid()) { 87 | ServerVerify.verifyInit(kv.getTelegrambottoken(), kv.getTelegramchatid()); 88 | } else if (null != kv.getServerpushkey()) { 89 | ServerVerify.verifyInit(kv.getServerpushkey()); 90 | } 91 | 92 | 93 | VersionInfo.printVersionInfo(); 94 | //每日任务65经验 95 | Config.getInstance().configInit(new Gson().toJson(kv)); 96 | if (!Config.getInstance().isSkipDailyTask()) { 97 | DailyTask dailyTask = new DailyTask(); 98 | dailyTask.doDailyTask(); 99 | } else { 100 | log.info("已开启了跳过本日任务,本日任务跳过(不会发起任何网络请求),如果需要取消跳过,请将skipDailyTask值改为false"); 101 | ServerPush.doServerPush(); 102 | } 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/ChargeMe.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.apiquery.oftenAPI; 7 | import top.misec.config.Config; 8 | import top.misec.login.Verify; 9 | import top.misec.utils.HelpUtil; 10 | import top.misec.utils.HttpUtil; 11 | 12 | import java.util.Calendar; 13 | import java.util.TimeZone; 14 | 15 | import static top.misec.task.TaskInfoHolder.*; 16 | 17 | /** 18 | * 给自己充电 19 | *

20 | * 月底自动给自己充电。仅充会到期的B币券,低于2的时候不会充 21 | * 22 | * @author @JunzhouLiu @Kurenai 23 | * @since 2020-11-22 5:43 24 | */ 25 | @Slf4j 26 | public class ChargeMe implements Task { 27 | 28 | private final String taskName = "大会员月底B币券充电和月初大会员权益领取"; 29 | 30 | @Override 31 | public void run() { 32 | Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT+8")); 33 | int day = cal.get(Calendar.DATE); 34 | //被充电用户的userID 35 | String userId = Verify.getInstance().getUserId(); 36 | String configChargeUserId = Config.getInstance().getChargeForLove(); 37 | 38 | //B币券余额 39 | double couponBalance; 40 | //大会员类型 41 | int vipType = queryVipStatusType(); 42 | 43 | if (vipType == 0 || vipType == 1) { 44 | log.info("普通会员和月度大会员每月不赠送B币券,所以没法给自己充电哦"); 45 | return; 46 | } 47 | 48 | if (!Config.getInstance().isMonthEndAutoCharge()) { 49 | log.info("未开启月底给自己充电功能"); 50 | return; 51 | } 52 | 53 | if (!"0".equals(configChargeUserId)) { 54 | String userName = oftenAPI.queryUserName(configChargeUserId); 55 | if ("1".equals(userName)) { 56 | userId = Verify.getInstance().getUserId(); 57 | log.info("充电对象已置为你本人"); 58 | } else { 59 | userId = Config.getInstance().getChargeForLove(); 60 | log.info("你配置的充电对象非本人而是: {}", HelpUtil.userNameEncode(userName)); 61 | } 62 | } else { 63 | log.info("你配置的充电对象是你本人没错了!"); 64 | } 65 | 66 | if (userInfo != null) { 67 | couponBalance = userInfo.getWallet().getCoupon_balance(); 68 | } else { 69 | JsonObject queryJson = HttpUtil.doGet(ApiList.chargeQuery + "?mid=" + userId); 70 | couponBalance = queryJson.getAsJsonObject("data").getAsJsonObject("bp_wallet").get("coupon_balance").getAsDouble(); 71 | } 72 | 73 | /* 74 | 判断条件 是月底&&是年大会员&&b币券余额大于2&&配置项允许自动充电 75 | */ 76 | if (day >= 28 && couponBalance >= 2 && 77 | Config.getInstance().isMonthEndAutoCharge()) { 78 | String requestBody = "bp_num=" + couponBalance 79 | + "&is_bp_remains_prior=true" 80 | + "&up_mid=" + userId 81 | + "&otype=up" 82 | + "&oid=" + userId 83 | + "&csrf=" + Verify.getInstance().getBiliJct(); 84 | 85 | JsonObject jsonObject = HttpUtil.doPost(ApiList.autoCharge, requestBody); 86 | 87 | int resultCode = jsonObject.get(STATUS_CODE_STR).getAsInt(); 88 | if (resultCode == 0) { 89 | JsonObject dataJson = jsonObject.get("data").getAsJsonObject(); 90 | int statusCode = dataJson.get("status").getAsInt(); 91 | if (statusCode == 4) { 92 | log.info("月底了,给自己充电成功啦,送的B币券没有浪费哦"); 93 | log.info("本次充值使用了: " + couponBalance + "个B币券"); 94 | //获取充电留言token 95 | String orderNo = dataJson.get("order_no").getAsString(); 96 | chargeComments(orderNo); 97 | } else { 98 | log.debug("充电失败了啊 原因: " + jsonObject); 99 | } 100 | 101 | } else { 102 | log.debug("充电失败了啊 原因: " + jsonObject); 103 | } 104 | } else { 105 | if (day < 28) { 106 | log.info("今天是本月的第: " + day + "天,还没到充电日子呢"); 107 | } else { 108 | log.info("本月已经充过电了,睿总送咱的B币券已经没有啦,下月再充啦"); 109 | } 110 | 111 | } 112 | } 113 | 114 | private void chargeComments(String token) { 115 | 116 | String requestBody = "order_id=" + token 117 | + "&message=" + "BILIBILI-HELPER自动充电" 118 | + "&csrf=" + Verify.getInstance().getBiliJct(); 119 | JsonObject jsonObject = HttpUtil.doPost(ApiList.chargeComment, requestBody); 120 | 121 | if (jsonObject.get(STATUS_CODE_STR).getAsInt() == 0) { 122 | log.info("充电留言成功"); 123 | } else { 124 | log.debug(jsonObject.get("message").getAsString()); 125 | } 126 | 127 | } 128 | 129 | @Override 130 | public String getName() { 131 | return taskName; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | top.misec 8 | BILIBILI-HELPER 9 | 1.4.1 10 | 11 | UTF-8 12 | 13 | 14 | 15 | 16 | org.apache.maven.plugins 17 | maven-compiler-plugin 18 | 3.8.1 19 | 20 | 8 21 | 8 22 | 23 | 24 | 25 | 26 | org.apache.maven.plugins 27 | maven-assembly-plugin 28 | 29 | true 30 | 31 | jar-with-dependencies 32 | 33 | 34 | 35 | top.misec.BiliMain 36 | 37 | 38 | 39 | 40 | 41 | make-assembly 42 | package 43 | 44 | single 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.apache.httpcomponents 58 | httpclient 59 | 4.5.12 60 | 61 | 62 | commons-logging 63 | commons-logging 64 | 65 | 66 | 67 | 68 | 69 | 70 | com.google.code.gson 71 | gson 72 | 2.8.5 73 | 74 | 75 | 76 | 77 | org.slf4j 78 | slf4j-api 79 | 1.7.30 80 | 81 | 82 | 83 | org.slf4j 84 | jcl-over-slf4j 85 | 1.7.30 86 | 87 | 88 | 89 | org.slf4j 90 | slf4j-jdk14 91 | 1.7.30 92 | 93 | 94 | 95 | org.apache.logging.log4j 96 | log4j-slf4j-impl 97 | 2.14.1 98 | 99 | 100 | 101 | org.apache.logging.log4j 102 | log4j-api 103 | 2.13.3 104 | 105 | 106 | org.apache.logging.log4j 107 | log4j-core 108 | 2.13.3 109 | 110 | 111 | 112 | 113 | 114 | junit 115 | junit 116 | 4.13.1 117 | test 118 | 119 | 120 | 121 | org.projectlombok 122 | lombok 123 | 1.18.16 124 | provided 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /docs/IMG/jetbrains.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 44 | 45 | 47 | 49 | 51 | 54 | 57 | 59 | 60 | 63 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/GetVideoId.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import lombok.Data; 7 | import lombok.extern.slf4j.Slf4j; 8 | import top.misec.apiquery.ApiList; 9 | import top.misec.login.Verify; 10 | import top.misec.utils.HttpUtil; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Random; 14 | import java.util.concurrent.ArrayBlockingQueue; 15 | 16 | /** 17 | * @author @JunzhouLiu 18 | * @create 2020/11/12 13:17 19 | */ 20 | 21 | @Slf4j 22 | @Data 23 | public class GetVideoId { 24 | private ArrayList followUpVideoList; 25 | private ArrayList rankVideoList; 26 | private ArrayBlockingQueue followUpVideoQueue; 27 | 28 | public GetVideoId() { 29 | this.followUpVideoList = queryDynamicNew(); 30 | this.rankVideoList = regionRanking(); 31 | videoUpdate("14602398"); 32 | if (this.followUpVideoList.size() > 0) { 33 | this.followUpVideoQueue = new ArrayBlockingQueue<>(followUpVideoList.size()); 34 | this.followUpVideoQueue.addAll(followUpVideoList); 35 | } 36 | } 37 | 38 | public void updateAllVideoList() { 39 | this.followUpVideoList = queryDynamicNew(); 40 | this.rankVideoList = regionRanking(); 41 | if (this.followUpVideoList.size() > 0) { 42 | this.followUpVideoQueue = new ArrayBlockingQueue<>(followUpVideoList.size()); 43 | this.followUpVideoQueue.addAll(followUpVideoList); 44 | } 45 | } 46 | 47 | /** 48 | * 从动态中获取随机bv号 49 | */ 50 | public String getFollowUpRandomVideoBvid() { 51 | if (followUpVideoList.size() == 0) { 52 | return getRegionRankingVideoBvid(); 53 | } 54 | Random random = new Random(); 55 | return followUpVideoList.get(random.nextInt(followUpVideoList.size())); 56 | } 57 | 58 | /** 59 | * 暂未启用的方法 60 | * 61 | * @return 从阻塞队列中获取bv号 62 | */ 63 | @ExtensionMethod 64 | public String getFollowUpRecentVideoBvid() { 65 | return followUpVideoQueue.peek() == null ? getRegionRankingVideoBvid() : followUpVideoQueue.poll(); 66 | } 67 | 68 | /** 69 | * 排行榜获取随机bv号 70 | */ 71 | public String getRegionRankingVideoBvid() { 72 | Random random = new Random(); 73 | return rankVideoList.get(random.nextInt(rankVideoList.size())); 74 | } 75 | 76 | public ArrayList queryDynamicNew() { 77 | ArrayList arrayList = new ArrayList<>(); 78 | String urlParameter = "?uid=" + Verify.getInstance().getUserId() 79 | + "&type_list=8" 80 | + "&from=" 81 | + "&platform=web"; 82 | JsonObject jsonObject = HttpUtil.doGet(ApiList.queryDynamicNew + urlParameter); 83 | JsonArray jsonArray = jsonObject.getAsJsonObject("data").getAsJsonArray("cards"); 84 | 85 | if (jsonArray != null) { 86 | for (JsonElement videoInfo : jsonArray) { 87 | JsonObject tempObject = videoInfo.getAsJsonObject().getAsJsonObject("desc"); 88 | arrayList.add(tempObject.get("bvid").getAsString()); 89 | } 90 | } 91 | return arrayList; 92 | } 93 | 94 | /** 95 | * 从有限分区中随机返回一个分区rid 96 | * 后续会更新请求分区 97 | * 98 | * @return regionId 分区id 99 | */ 100 | public int randomRegion() { 101 | int[] arr = {1, 3, 4, 5, 160, 22, 119}; 102 | return arr[(int) (Math.random() * arr.length)]; 103 | } 104 | 105 | /** 106 | * 默认请求动画区,3日榜单 107 | */ 108 | public ArrayList regionRanking() { 109 | int rid = randomRegion(); 110 | int day = 3; 111 | return regionRanking(rid, day); 112 | } 113 | 114 | /** 115 | * @param rid 分区id 默认为3 116 | * @param day 日榜,三日榜 周榜 1,3,7 117 | * @return 随机返回一个aid 118 | */ 119 | public ArrayList regionRanking(int rid, int day) { 120 | 121 | ArrayList videoList = new ArrayList<>(); 122 | String urlParam = "?rid=" + rid + "&day=" + day; 123 | JsonObject resultJson = HttpUtil.doGet(ApiList.getRegionRanking + urlParam); 124 | 125 | JsonArray jsonArray = resultJson.getAsJsonArray("data"); 126 | 127 | if (jsonArray != null) { 128 | for (JsonElement videoInfo : jsonArray) { 129 | JsonObject tempObject = videoInfo.getAsJsonObject(); 130 | videoList.add(tempObject.get("bvid").getAsString()); 131 | } 132 | } 133 | return videoList; 134 | } 135 | public void videoUpdate(String mid){ 136 | String urlParam = "?mid=" + mid + "&ps=30&tid=0&pn=1&keyword=&order=pubdate&jsonp=jsonp"; 137 | JsonObject resultJson = HttpUtil.doGet(ApiList.getBvidByCreate + urlParam); 138 | JsonArray jsonArray=resultJson.getAsJsonObject("data").getAsJsonObject("list").getAsJsonArray("vlist"); 139 | 140 | if (jsonArray != null) { 141 | for (JsonElement videoInfo : jsonArray) { 142 | String bvid=videoInfo.getAsJsonObject().get("bvid").getAsString(); 143 | if(!CoinAdd.isCoin(bvid)){ 144 | this.rankVideoList.add(bvid); 145 | this.followUpVideoList.add(bvid); 146 | } 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/CoinAdd.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonObject; 4 | import lombok.extern.slf4j.Slf4j; 5 | import top.misec.apiquery.ApiList; 6 | import top.misec.apiquery.oftenAPI; 7 | import top.misec.config.Config; 8 | import top.misec.login.Verify; 9 | import top.misec.utils.HttpUtil; 10 | 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.Random; 14 | 15 | import static top.misec.task.TaskInfoHolder.STATUS_CODE_STR; 16 | import static top.misec.task.TaskInfoHolder.getVideoId; 17 | 18 | /** 19 | * 投币任务 20 | * 21 | * @author @JunzhouLiu @Kurenai 22 | * @since 2020-11-22 5:28 23 | */ 24 | @Slf4j 25 | public class CoinAdd implements Task { 26 | 27 | private final String taskName = "投币任务"; 28 | 29 | @Override 30 | public void run() { 31 | 32 | //投币最多操作数 解决csrf校验失败时死循环的问题 33 | int addCoinOperateCount = 0; 34 | //安全检查,最多投币数 35 | final int maxNumberOfCoins = 5; 36 | //获取自定义配置投币数 配置写在src/main/resources/config.json中 37 | int setCoin = Config.getInstance().getNumberOfCoins(); 38 | // 预留硬币数 39 | int reserveCoins = Config.getInstance().getReserveCoins(); 40 | 41 | //已投的硬币 42 | int useCoin = TaskInfoHolder.expConfirm(); 43 | //投币策略 44 | int coinAddPriority = Config.getInstance().getCoinAddPriority(); 45 | 46 | if (setCoin > maxNumberOfCoins) { 47 | log.info("自定义投币数为: " + setCoin + "枚," + "为保护你的资产,自定义投币数重置为: " + maxNumberOfCoins + "枚"); 48 | setCoin = maxNumberOfCoins; 49 | } 50 | 51 | log.info("自定义投币数为: " + setCoin + "枚," + "程序执行前已投: " + useCoin + "枚"); 52 | 53 | //调整投币数 设置投币数-已经投过的硬币数 54 | int needCoins = setCoin - useCoin; 55 | 56 | //投币前硬币余额 57 | Double beforeAddCoinBalance = oftenAPI.getCoinBalance(); 58 | int coinBalance = (int) Math.floor(beforeAddCoinBalance); 59 | 60 | 61 | if (needCoins <= 0) { 62 | log.info("已完成设定的投币任务,今日无需再投币了"); 63 | // return; 64 | } else { 65 | log.info("投币数调整为: " + needCoins + "枚"); 66 | //投币数大于余额时,按余额投 67 | if (needCoins > coinBalance) { 68 | log.info("完成今日设定投币任务还需要投: " + needCoins + "枚硬币,但是余额只有: " + beforeAddCoinBalance); 69 | log.info("投币数调整为: " + coinBalance); 70 | needCoins = coinBalance; 71 | } 72 | } 73 | 74 | if (coinBalance < reserveCoins) { 75 | log.info("剩余硬币数为{},低于预留硬币数{},今日不再投币", beforeAddCoinBalance, reserveCoins); 76 | log.info("tips: 当硬币余额少于你配置的预留硬币数时,则会暂停当日投币任务"); 77 | return; 78 | } 79 | 80 | log.info("投币前余额为 : " + beforeAddCoinBalance); 81 | /* 82 | * 开始投币 83 | * 请勿修改 max_numberOfCoins 这里多判断一次保证投币数超过5时 不执行投币操作 84 | * 最后一道安全判断,保证即使前面的判断逻辑错了,也不至于发生投币事故 85 | */ 86 | while (needCoins > 0 && needCoins <= maxNumberOfCoins) { 87 | String bvid; 88 | 89 | if (coinAddPriority == 1 && addCoinOperateCount < 7) { 90 | bvid = getVideoId.getFollowUpRandomVideoBvid(); 91 | } else { 92 | bvid = getVideoId.getRegionRankingVideoBvid(); 93 | } 94 | 95 | addCoinOperateCount++; 96 | boolean flag = coinAdd(bvid, 1, Config.getInstance().getSelectLike()); 97 | if (flag) { 98 | try { 99 | Random random = new Random(); 100 | int sleepTime = (int) ((random.nextDouble() + 0.5) * 3000); 101 | log.info("投币后随机暂停{}毫秒", sleepTime); 102 | Thread.sleep(sleepTime); 103 | } catch (InterruptedException e) { 104 | e.printStackTrace(); 105 | } 106 | needCoins--; 107 | } 108 | if (addCoinOperateCount > 15) { 109 | log.info("尝试投币/投币失败次数太多"); 110 | break; 111 | } 112 | } 113 | log.info("投币任务完成后余额为: " + oftenAPI.getCoinBalance()); 114 | } 115 | 116 | /** 117 | * @param bvid av号 118 | * @param multiply 投币数量 119 | * @param selectLike 是否同时点赞 1是 120 | * @return 是否投币成功 121 | */ 122 | private boolean coinAdd(String bvid, int multiply, int selectLike) { 123 | String requestBody = "bvid=" + bvid 124 | + "&multiply=" + multiply 125 | + "&select_like=" + selectLike 126 | + "&cross_domain=" + "true" 127 | + "&csrf=" + Verify.getInstance().getBiliJct(); 128 | String videoTitle = oftenAPI.videoTitle(bvid); 129 | //判断曾经是否对此av投币过 130 | if (!isCoin(bvid)) { 131 | Map headers = new HashMap<>(); 132 | headers.put("Referer", "https://www.bilibili.com/video/" + bvid); 133 | headers.put("Origin", "https://www.bilibili.com"); 134 | JsonObject jsonObject = HttpUtil.doPost(ApiList.CoinAdd, requestBody, headers); 135 | if (jsonObject.get(STATUS_CODE_STR).getAsInt() == 0) { 136 | 137 | log.info("为 " + videoTitle + " 投币成功"); 138 | return true; 139 | } else { 140 | log.info("投币失败" + jsonObject.get("message").getAsString()); 141 | return false; 142 | } 143 | } else { 144 | log.debug("已经为" + videoTitle + "投过币了"); 145 | return false; 146 | } 147 | } 148 | 149 | /** 150 | * 检查是否投币 151 | * 152 | * @param bvid av号 153 | * @return 返回是否投过硬币了 154 | */ 155 | static boolean isCoin(String bvid) { 156 | String urlParam = "?bvid=" + bvid; 157 | JsonObject result = HttpUtil.doGet(ApiList.isCoin + urlParam); 158 | 159 | int multiply = result.getAsJsonObject("data").get("multiply").getAsInt(); 160 | if (multiply > 0) { 161 | log.info("之前已经为av" + bvid + "投过" + multiply + "枚硬币啦"); 162 | return true; 163 | } else { 164 | return false; 165 | } 166 | } 167 | 168 | @Override 169 | public String getName() { 170 | return taskName; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/top/misec/utils/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package top.misec.utils; 2 | 3 | import com.google.gson.JsonElement; 4 | import com.google.gson.JsonObject; 5 | import com.google.gson.JsonParser; 6 | import lombok.Data; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.http.HttpEntity; 9 | import org.apache.http.NameValuePair; 10 | import org.apache.http.client.config.RequestConfig; 11 | import org.apache.http.client.methods.CloseableHttpResponse; 12 | import org.apache.http.client.methods.HttpGet; 13 | import org.apache.http.client.methods.HttpPost; 14 | import org.apache.http.entity.StringEntity; 15 | import org.apache.http.impl.client.CloseableHttpClient; 16 | import org.apache.http.impl.client.HttpClients; 17 | import org.apache.http.message.BasicNameValuePair; 18 | import org.apache.http.util.EntityUtils; 19 | import top.misec.login.Verify; 20 | 21 | import java.io.IOException; 22 | import java.util.Map; 23 | import java.util.Optional; 24 | 25 | /** 26 | * @author Junzhou Liu 27 | * @create 2020/10/11 4:03 28 | */ 29 | 30 | @Slf4j 31 | @Data 32 | public class HttpUtil { 33 | /** 34 | * 设置配置请求参数 35 | * 设置连接主机服务超时时间 36 | * 设置连接请求超时时间 37 | * 设置读取数据连接超时时间 38 | */ 39 | private static final RequestConfig REQUEST_CONFIG = RequestConfig.custom().setConnectTimeout(5000) 40 | .setConnectionRequestTimeout(5000) 41 | .setSocketTimeout(10000) 42 | .build(); 43 | static Verify verify = Verify.getInstance(); 44 | private static String userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) " + 45 | "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.54"; 46 | private static CloseableHttpClient httpClient = null; 47 | private static CloseableHttpResponse httpResponse = null; 48 | 49 | public static JsonObject doPost(String url, JsonObject jsonObject) { 50 | return doPost(url, jsonObject.toString()); 51 | } 52 | 53 | public static JsonObject doPost(String url, String requestBody) { 54 | return doPost(url, requestBody, null); 55 | } 56 | 57 | public static JsonObject doPost(String url, String requestBody, Map headers) { 58 | httpClient = HttpClients.createDefault(); 59 | JsonObject resultJson = null; 60 | // 创建httpPost远程连接实例 61 | HttpPost httpPost = new HttpPost(url); 62 | // 设置请求头 63 | httpPost.setConfig(REQUEST_CONFIG); 64 | /* 65 | addHeader:添加一个新的请求头字段。(一个请求头中允许有重名字段。) 66 | setHeader:设置一个请求头字段,有则覆盖,无则添加。 67 | 有什么好的方式判断key1=value和{"key1":"value"} 68 | */ 69 | if (requestBody.startsWith("{")) { 70 | //java的正则表达式咋写...... 71 | httpPost.setHeader("Content-Type", "application/json"); 72 | } else { 73 | httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); 74 | } 75 | httpPost.setHeader("Connection", "keep-alive"); 76 | httpPost.setHeader("User-Agent", userAgent); 77 | httpPost.setHeader("Cookie", verify.getVerify()); 78 | 79 | if (null != headers && !headers.isEmpty()) { 80 | for (String key : headers.keySet()) { 81 | httpPost.setHeader(key, headers.get(key)); 82 | } 83 | } else { 84 | httpPost.setHeader("Referer", "https://www.bilibili.com/"); 85 | } 86 | // 封装post请求参数 87 | 88 | StringEntity stringEntity = new StringEntity(requestBody, "utf-8"); 89 | 90 | httpPost.setEntity(stringEntity); 91 | 92 | try { 93 | // httpClient对象执行post请求,并返回响应参数对象 94 | httpResponse = httpClient.execute(httpPost); 95 | resultJson = processResult(httpResponse); 96 | } catch (Exception e) { 97 | log.error("", e); 98 | e.printStackTrace(); 99 | } finally { 100 | httpResource(httpClient, httpResponse); 101 | } 102 | return resultJson; 103 | } 104 | 105 | public static JsonObject doGet(String url) { 106 | return doGet(url, new JsonObject()); 107 | } 108 | 109 | private static NameValuePair getNameValuePair(Map.Entry entry) { 110 | return new BasicNameValuePair(entry.getKey(), Optional.ofNullable(entry.getValue()).map(Object::toString).orElse(null)); 111 | } 112 | 113 | public static NameValuePair[] getPairList(JsonObject pJson) { 114 | return pJson.entrySet().parallelStream().map(HttpUtil::getNameValuePair).toArray(NameValuePair[]::new); 115 | } 116 | 117 | public static JsonObject doGet(String url, JsonObject pJson) { 118 | // 通过址默认配置创建一个httpClient实例 119 | httpClient = HttpClients.createDefault(); 120 | JsonObject resultJson = null; 121 | try { 122 | // 创建httpGet远程连接实例 123 | HttpGet httpGet = new HttpGet(url); 124 | // 设置请求头信息,鉴权 125 | httpGet.setHeader("Connection", "keep-alive"); 126 | httpGet.setHeader("User-Agent", userAgent); 127 | httpGet.setHeader("Cookie", verify.getVerify()); 128 | for (NameValuePair pair : getPairList(pJson)) { 129 | httpGet.setHeader(pair.getName(), pair.getValue()); 130 | } 131 | // 为httpGet实例设置配置 132 | httpGet.setConfig(REQUEST_CONFIG); 133 | // 执行get请求得到返回对象 134 | httpResponse = httpClient.execute(httpGet); 135 | resultJson = processResult(httpResponse); 136 | } catch (Exception e) { 137 | e.printStackTrace(); 138 | } finally { 139 | // 关闭资源 140 | httpResource(httpClient, httpResponse); 141 | } 142 | return resultJson; 143 | 144 | } 145 | 146 | public static JsonObject processResult(CloseableHttpResponse httpResponse) throws IOException { 147 | JsonObject resultJson = null; 148 | if (httpResponse != null) { 149 | int responseStatusCode = httpResponse.getStatusLine().getStatusCode(); 150 | // 从响应对象中获取响应内容 151 | // 通过返回对象获取返回数据 152 | HttpEntity entity = httpResponse.getEntity(); 153 | // 通过EntityUtils中的toString方法将结果转换为字符串 154 | String result = EntityUtils.toString(entity); 155 | resultJson = new JsonParser().parse(result).getAsJsonObject(); 156 | switch (responseStatusCode) { 157 | case 200: 158 | break; 159 | case 412: 160 | log.debug("{}", httpResponse.getStatusLine()); 161 | break; 162 | default: 163 | } 164 | } 165 | return resultJson; 166 | } 167 | 168 | 169 | private static void httpResource(CloseableHttpClient httpClient, CloseableHttpResponse response) { 170 | if (null != response) { 171 | try { 172 | response.close(); 173 | } catch (IOException e) { 174 | e.printStackTrace(); 175 | } 176 | } 177 | if (null != httpClient) { 178 | try { 179 | httpClient.close(); 180 | } catch (IOException e) { 181 | e.printStackTrace(); 182 | } 183 | } 184 | } 185 | 186 | public static void setUserAgent(String userAgent) { 187 | HttpUtil.userAgent = userAgent; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/top/misec/task/GiveGift.java: -------------------------------------------------------------------------------- 1 | package top.misec.task; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonObject; 5 | import lombok.extern.slf4j.Slf4j; 6 | import top.misec.config.Config; 7 | import top.misec.login.Verify; 8 | import top.misec.utils.HttpUtil; 9 | 10 | /** 11 | * B站直播送出即将过期的礼物 12 | * 13 | * @author srcrs 14 | * @Time 2020-10-13 15 | */ 16 | 17 | @Slf4j 18 | public class GiveGift implements Task { 19 | 20 | private final String taskName = "B站直播送出即将过期的礼物"; 21 | /** 22 | * 获取日志记录器对象 23 | */ 24 | Config config = Config.getInstance(); 25 | 26 | @Override 27 | public void run() { 28 | try { 29 | /* 从配置类中读取是否需要执行赠送礼物 */ 30 | if (!config.isGiveGift()) { 31 | log.info("未开启自动送出即将过期礼物功能"); 32 | return; 33 | } 34 | /* 直播间 id */ 35 | String roomId = ""; 36 | /* 直播间 uid 即 up 的 id*/ 37 | String uid = ""; 38 | /* B站后台时间戳为10位 */ 39 | long nowTime = System.currentTimeMillis() / 1000; 40 | /* 获得礼物列表 */ 41 | JsonArray jsonArray = xliveGiftBagList(); 42 | /* 判断是否有过期礼物出现 */ 43 | boolean flag = true; 44 | for (Object object : jsonArray) { 45 | JsonObject json = (JsonObject) object; 46 | long expireAt = Long.parseLong(json.get("expire_at").getAsString()); 47 | /* 礼物还剩 1 天送出 */ 48 | /* 永久礼物到期时间为 0 */ 49 | if ((expireAt - nowTime) < 60 * 60 * 25 * 1 && expireAt != 0) { 50 | /* 如果有未送出的礼物,则获取一个直播间 */ 51 | if ("".equals(roomId)) { 52 | JsonObject uidAndRid = getuidAndRid(); 53 | uid = uidAndRid.get("uid").getAsString(); 54 | roomId = uidAndRid.get("roomid").getAsString(); 55 | } 56 | 57 | String requestBody = "biz_id=" + roomId + 58 | "&ruid=" + uid + 59 | "&bag_id=" + json.get("bag_id") + 60 | "&gift_id=" + json.get("gift_id") + 61 | "&gift_num=" + json.get("gift_num"); 62 | JsonObject jsonObject3 = xliveBagSend(requestBody); 63 | if ("0".equals(jsonObject3.get("code").getAsString())) { 64 | /* 礼物的名字 */ 65 | String giftName = jsonObject3.get("data").getAsJsonObject().get("gift_name").getAsString(); 66 | /* 礼物的数量 */ 67 | String giftNum = jsonObject3.get("data").getAsJsonObject().get("gift_num").getAsString(); 68 | log.info("给直播间 - {} - {} - 数量: {}✔", roomId, giftName, giftNum); 69 | flag = false; 70 | } else { 71 | log.debug("送礼失败, 原因 : {}❌", jsonObject3); 72 | } 73 | } 74 | } 75 | if (flag) { 76 | log.info("当前无即将过期礼物❌"); 77 | } 78 | } catch (Exception e) { 79 | log.error("💔赠送礼物异常 : ", e); 80 | } 81 | } 82 | 83 | /** 84 | * 获取一个直播间的room_id 85 | * 86 | * @return String 87 | * @author srcrs 88 | * @Time 2020-10-13 89 | */ 90 | public String xliveGetRecommend() { 91 | return HttpUtil.doGet("https://api.live.bilibili.com/relation/v1/AppWeb/getRecommendList") 92 | .get("data").getAsJsonObject() 93 | .get("list").getAsJsonArray() 94 | .get(6).getAsJsonObject() 95 | .get("roomid").getAsString(); 96 | } 97 | 98 | /** 99 | * B站获取直播间的uid 100 | * 101 | * @param roomId up 主的 uid 102 | * @return String 103 | * @author srcrs 104 | * @Time 2020-10-13 105 | */ 106 | public String xliveGetRoomUid(String roomId) { 107 | JsonObject pJson = new JsonObject(); 108 | pJson.addProperty("room_id", roomId); 109 | String urlPram = "?room_id=" + roomId; 110 | return HttpUtil.doGet("https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom" + urlPram) 111 | .get("data").getAsJsonObject() 112 | .get("room_info").getAsJsonObject() 113 | .get("uid").getAsString(); 114 | } 115 | 116 | /** 117 | * 根据 uid 获取其 roomid 118 | * 119 | * @param mid 即 uid 120 | * @return String 返回一个直播间id 121 | * @author srcrs 122 | * @Time 2020-11-20 123 | */ 124 | public String getRoomInfoOld(String mid) { 125 | JsonObject pJson = new JsonObject(); 126 | pJson.addProperty("mid", Integer.parseInt(mid)); 127 | String urlPram = "?mid=" + mid; 128 | return HttpUtil.doGet("http://api.live.bilibili.com/room/v1/Room/getRoomInfoOld" + urlPram) 129 | .get("data").getAsJsonObject() 130 | .get("roomid").getAsString(); 131 | } 132 | 133 | /** 134 | * B站直播获取背包礼物 135 | * 136 | * @return JsonArray 137 | * @author srcrs 138 | * @Time 2020-10-13 139 | */ 140 | public JsonArray xliveGiftBagList() { 141 | return HttpUtil.doGet("https://api.live.bilibili.com/xlive/web-room/v1/gift/bag_list") 142 | .get("data").getAsJsonObject() 143 | .get("list").getAsJsonArray(); 144 | } 145 | 146 | /** 147 | * B站直播送出背包的礼物 148 | * 149 | * @param requestBody 150 | * @return JsonObject 151 | * @author srcrs 152 | * @Time 2020-10-13 153 | */ 154 | public JsonObject xliveBagSend(String requestBody) { 155 | 156 | requestBody += "&uid=" + Verify.getInstance().getUserId() + 157 | "&csrf=" + Verify.getInstance().getBiliJct() + 158 | "&send_ruid=" + "0" + 159 | "&storm_beat_id=" + "0" + 160 | "&price=" + "0" + 161 | "&platform=" + "pc" + 162 | "&biz_code=" + "live"; 163 | 164 | return HttpUtil.doPost("https://api.live.bilibili.com/gift/v2/live/bag_send", requestBody); 165 | } 166 | 167 | /** 168 | * 获取一个包含 uid 和 RooId 的 json 对象 169 | * 170 | * @return JsonObject 返回一个包含 uid 和 RooId 的 json 对象 171 | * @author srcrs 172 | * @Time 2020-11-20 173 | */ 174 | public JsonObject getuidAndRid() { 175 | /* 直播间 id */ 176 | String roomId; 177 | /* 直播间 uid 即 up 的 id*/ 178 | String uid; 179 | if (!config.getUpLive().equals("0")) { 180 | /* 获取指定up的id */ 181 | uid = config.getUpLive(); 182 | roomId = getRoomInfoOld(uid); 183 | String status = "0"; 184 | if (status.equals(roomId)) { 185 | log.info("自定义up {} 无直播间", uid); 186 | /* 随机获取一个直播间 */ 187 | roomId = xliveGetRecommend(); 188 | uid = xliveGetRoomUid(roomId); 189 | log.info("随机直播间"); 190 | } else { 191 | log.info("自定义up {} 的直播间", uid); 192 | } 193 | 194 | } else { 195 | /* 随机获取一个直播间 */ 196 | roomId = xliveGetRecommend(); 197 | uid = xliveGetRoomUid(roomId); 198 | log.info("随机直播间"); 199 | } 200 | JsonObject json = new JsonObject(); 201 | json.addProperty("uid", uid); 202 | json.addProperty("roomid", roomId); 203 | return json; 204 | } 205 | 206 | @Override 207 | public String getName() { 208 | return taskName; 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | BILIBILI-HELPER-PRE 4 |

5 | 6 | [![GitHub stars](https://img.shields.io/github/stars/JunzhouLiu/BILIBILI-HELPER-PRE?style=flat-square)](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/stargazers) 7 | [![GitHub forks](https://img.shields.io/github/forks/JunzhouLiu/BILIBILI-HELPER-PRE?style=flat-square)](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/network) 8 | [![GitHub issues](https://img.shields.io/github/issues/JunzhouLiu/BILIBILI-HELPER-PRE?style=flat-square)](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/issues) 9 | [![GitHub license](https://img.shields.io/github/license/JunzhouLiu/BILIBILI-HELPER-PRE?style=flat-square)](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/blob/main/LICENSE) 10 | [![GitHub All Releases](https://img.shields.io/github/downloads/JunzhouLiu/BILIBILI-HELPER-PRE/total?style=flat-square)](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/releases) 11 | [![Docker Pulls](https://img.shields.io/docker/pulls/superng6/bilibili-helper?style=flat-square)](https://hub.docker.com/r/superng6/bilibili-helper) 12 | [![GitHub contributors](https://img.shields.io/github/contributors/JunzhouLiu/BILIBILI-HELPER-PRE?style=flat-square)](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/graphs/contributors) 13 | ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/JunzhouLiu/BILIBILI-HELPER-PRE?style=flat-square) 14 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FJunzhouLiu%2FBILIBILI-HELPER.svg?type=flat-square)](https://app.fossa.com/projects/git%2Bgithub.com%2FJunzhouLiu%2FBILIBILI-HELPER?ref=badge_shield) 15 | 16 |
17 | 18 | ## 工具简介 19 | 20 | 原工具被 ban,目测是因为 GitHub actions,正在和 github 沟通,希望能够尽快恢复,本仓库版本移除了对 github actions 的支持。 21 | 22 | 这是一个利用 Linux Crontab , Docker 等方式实现哔哩哔哩(Bilibili)每日任务投币,点赞,分享视频,直播签到,银瓜子兑换硬币,漫画每日签到,简单配置即可每日轻松获取 65 经验值,快来和我一起成为 23 | Lv6 吧\~\~\~\~ 24 | 25 | **如果觉得好用,顺手点个 Star 吧 ❤** 26 | 27 | **仓库地址:[JunzhouLiu/BILIBILI-HELPER-PRE](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE)** 28 | 29 | **B 站赛事预测助手已发布,每天自动参与 KPL,LPL 赛事预测,赚取硬币。** 30 | 31 | **仓库地址:[JunzhouLiu/bilibili-match-prediction](https://github.com/JunzhouLiu/bilibili-match-prediction)** 32 | 33 | **请不要滥用相关 API,让我们一起爱护 B 站 ❤** 34 | 35 |
36 | 37 | [也可点击此处一键加群](https://qm.qq.com/cgi-bin/qm/qr?k=m_M1Fydi3MvrVAEM0Sp6hDfZF4N2SpXU&jump_from=webapi) 38 | 39 | qq 群二维码 40 | 41 | ![qq群二维码](docs/IMG/qqgroup.png) 42 | 43 |
44 | 45 | ## 功能列表 46 | 47 | - [x] 每天上午 9 点 10 分自动开始任务。_【运行时间可自定义】_ 48 | - [x] 哔哩哔哩漫画每日自动签到,自动阅读 1 章节 。 49 | - [x] 每日自动从热门视频中随机观看 1 个视频,分享一个视频。 50 | - [x] 每日从热门视频中选取 5 个进行智能投币 _【如果投币不能获得经验了,则不会投币】_ 51 | - [x] 投币支持下次一定啦,可自定义每日投币数量。_【如果检测到你已经投过币了,则不会投币】_ 52 | - [x] 大会员月底使用快到期的 B 币券,给自己充电,一点也不会浪费哦,默认开启。_【已支持给指定 UP 充电】_ 53 | - [x] 大会员月初 1 号自动领取每月 5 张 B 币券和福利。 54 | - [x] 每日哔哩哔哩直播自动签到,领取签到奖励。_【直播你可以不看,但是奖励咱们一定要领】_ 55 | - [x] Linux 用户支持自定义配置了。 56 | - [x] 投币策略更新可配置投币喜好。_【可配置优先给关注的 up 投币】_ 57 | - [x] 自动送出即将过期的礼物。 _【默认开启,未更新到新版本的用户默认关闭】_ 58 | - [x] 支持推送执行结果到微信,钉钉,飞书等 59 | 60 | [点击快速开始使用](#使用说明) 61 | 62 | [点击快速查看自定义功能配置](#自定义功能配置) 63 | 64 | # 目录 65 | 66 | - [目录](#目录) 67 | - [使用说明](#使用说明) 68 | - [获取运行所需的 Cookies](#获取运行所需的-cookies) 69 | - [一、使用 腾讯云函数](#一使用-腾讯云函数) 70 | - [二、使用 Docker](#二使用-docker) 71 | - [三、使用 Linux Crontab 方式](#三使用-linux-crontab-方式) 72 | - [自定义功能配置](#自定义功能配置) 73 | - [订阅执行结果](#订阅执行结果) 74 | - [Server 酱 Turbo 版](#server-酱-turbo-版) 75 | - [Telegram 订阅执行结果](#telegram-订阅执行结果) 76 | - [钉钉机器人](#钉钉机器人) 77 | - [PushPlus(Push+)](#pushpluspush) 78 | - [更新和帮助](#更新和帮助) 79 | - [使用 Pull APP[推荐]](#使用-pull-app推荐) 80 | - [常见问题解答](#常见问题解答) 81 | - [免责声明](#免责声明) 82 | - [API 参考列表](#api-参考列表) 83 | - [基于本项目的衍生项目](#基于本项目的衍生项目) 84 | - [致谢](#致谢) 85 | - [License](#license) 86 | - [Stargazers over time](#stargazers-over-time) 87 | 88 | ## 使用说明 89 | 90 | ### 获取运行所需的 Cookies 91 | 92 | 1. **Fork 本项目** 93 | 2. **获取 Bilibili Cookies** 94 | 3. 浏览器打开并登录 [bilibili 网站]() 95 | 4. 按 F12 打开 「开发者工具」 找到 应用程序/Application -\> 存储 -\> Cookies 96 | 5. 找到 `bili_jct` `SESSDATA` `DEDEUSERID` 三项,并复制值,后面需要用到。 97 | 98 | ![图示](docs/IMG/20201012001307.png) 99 | 100 | **请各位使用 Actions 时务必遵守 Github 条款。不要滥用 Actions 服务。** 101 | 102 | **Please be sure to abide by the Github terms when using Actions. Do not abuse the Actions service.** 103 | 104 | ### 一、使用 腾讯云函数 105 | 106 | 关于腾讯云 云函数功能开通相关问题 请加群询问。 107 | 108 | 腾讯云函数地址:[函数服务 - Serverless - 控制台 (tencent.com)](https://console.cloud.tencent.com/scf/list?rid=4&ns=default) 109 | 110 | 1.新建 111 | 112 | ![图示](docs/IMG/a0.jpg) 113 | 114 | 2.按照图示填写信息 115 | 116 | 执行方法:`top.misec.BiliMain::mainHandler` 117 | 118 | JAR包获取地址:[Release](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/releases) 119 | 120 | ![图示](docs/IMG/a.jpg) 121 | 122 | key:`scfFlag` value:`true` 123 | 124 | value配置文件: 125 | 126 | ```json 127 | { 128 | "numberOfCoins": 5, 129 | "reserveCoins": 50, 130 | "selectLike": 0, 131 | "monthEndAutoCharge": true, 132 | "giveGift": true, 133 | "upLive": "0", 134 | "chargeForLove": "0", 135 | "devicePlatform": "ios", 136 | "coinAddPriority": 1, 137 | "skipDailyTask": true, 138 | "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15", 139 | "dedeuserid": "", 140 | "sessdata": "", 141 | "biliJct": "", 142 | "telegrambottoken": null, 143 | "telegramchatid": null, 144 | "serverpushkey": null 145 | } 146 | ``` 147 | 148 | **dedeuserid sessdata biliJct 必填** 149 | 150 | **不使用TG推送请把telegrambottoken和telegramchatid的值改为null(上面示例就是null)** 151 | 152 | **不推送请把serverpushkey值改为null(上面示例就是null)** 153 | 154 | 例子: 155 | 156 | ```json 157 | { 158 | "numberOfCoins": 5, 159 | "reserveCoins": 50, 160 | "selectLike": 0, 161 | "monthEndAutoCharge": true, 162 | "giveGift": true, 163 | "upLive": "0", 164 | "chargeForLove": "0", 165 | "devicePlatform": "ios", 166 | "coinAddPriority": 1, 167 | "skipDailyTask": true, 168 | "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15", 169 | "dedeuserid": "", 170 | "sessdata": "", 171 | "biliJct": "", 172 | "telegrambottoken": null, 173 | "telegramchatid": null, 174 | "serverpushkey": "https://oapi.dingtalk.com/robot/send?access_token=XXX" 175 | } 176 | ``` 177 | SERVER酱: 178 | ```json 179 | { 180 | "numberOfCoins": 5, 181 | "reserveCoins": 50, 182 | "selectLike": 0, 183 | "monthEndAutoCharge": true, 184 | "giveGift": true, 185 | "upLive": "0", 186 | "chargeForLove": "0", 187 | "devicePlatform": "ios", 188 | "coinAddPriority": 1, 189 | "skipDailyTask": true, 190 | "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15", 191 | "dedeuserid": "", 192 | "sessdata": "", 193 | "biliJct": "", 194 | "telegrambottoken": null, 195 | "telegramchatid": null, 196 | "serverpushkey": "申请的KEY" 197 | } 198 | ``` 199 | 200 | [具体推送配置请点这](#订阅执行结果) 201 | 202 | **日志配置 不需要像下边图里一样,直接默认就行** 203 | 204 | ![图示](docs/IMG/b.jpg) 205 | 206 | 3.点完成后,再点击立即跳转 207 | 208 | ![图示](docs/IMG/c.jpg) 209 | 210 | 4.创建个触发器 211 | 212 | 点 触发管理->创建触发器 213 | 214 | CRON表达式:`30 09 * * *` 215 | 216 | ![图示](docs/IMG/d.jpg) 217 | 218 | 5.完成 219 | 220 | ### 二、使用 Docker 221 | 222 | 请自行参阅 [Issues/75#issuecomment-731705657][28] 和[基于本项目的衍生项目](#基于本项目的衍生项目)。 223 | 224 | [28]: https://github.com/JunzhouLiu/BILIBILI-HELPER/issues/75#issuecomment-731705657 225 | 226 | ### 三、使用 Linux Crontab 方式 227 | 228 | 1. 在 linux shell 环境执行以下命令,并按照提示输入 SESSDATA,DEDEUSERID,BILI_JCT,SCKEY 四个参数 229 | 230 | ``` 231 | wget https://raw.githubusercontent.com/JunzhouLiu/BILIBILI-HELPER-PRE/main/setup.sh && chmod +x ./setup.sh && sudo ./setup.sh 232 | ``` 233 | 234 | **ps:注意,如果使用自定义配置,请将`config.json`和 jar 包放置在同一目录(使用 setup.sh 安装则需要将`config.json`放置到`{HOME}/BILIBILI-HELPER`),`v1.2.2` 235 | 之后的版本`release`中都会携带一份`config.json`。** 236 | 237 | 2. 除此之外,也可以通过点击 [BILIBILI-HELPER-PRE/release][30],下载已发布的版本,解压后将 jar 包手动上传到 Linux 服务器,使用 crontab 完成定时执行,如果使用`crontab` 238 | 请记得`source /etc/profile`和`source ~/.bashrc`,建议直接使用仓库提供的[`start.sh`][31]脚本,注意修改脚本的 jar 包路径和 cookies 参数。 239 | 240 | [30]: https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/releases/latest 241 | [31]: https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/blob/main/start.sh 242 | 243 | **crontab 命令示例** 244 | 245 | `30 10 * * * sh /home/start.sh` 246 | 247 | | args | 说明 | 248 | | ----------------- | ------------------ | 249 | | 30 10 \* \* \* | `crontab` 定时时间 | 250 | | sh /home/start.sh | `start.sh`的路径 | 251 | 252 | ```shell 253 | #!/bin/bash 254 | source /etc/profile 255 | source ~/.bashrc 256 | source ~/.zshrc #其他终端请自行引入环境变量 257 | echo $PATH 258 | java -jar /home/BILIBILI-HELPER.jar DEDEUSERID SESSDATA BILI_JCT SCKEY >> /var/log/bilibili-help.log 259 | # 注意将jar包路径替换为实际路径。将参数修改该你自己的参数,cookies中含有等特殊字符需要转义。 260 | ``` 261 | 262 | **命令示例:** 263 | 264 | ```shell 265 | # *如果Cookies参数中包含特殊字符,例如`%`请使用`\`转义*,如果不执行可在命令前增加 source /etc/profile 266 | # m h dom mon dow command 267 | 30 10 * * * java -jar /home/BILIBILI-HELP.jar DEDEUSERID SESSDATA BILI_JCT >/var/log/cron.log & 268 | ``` 269 | 270 | ### 自定义功能配置 271 | 272 | 配置文件示例: 273 | 274 | ```json 275 | { 276 | "numberOfCoins": 5, 277 | "reserveCoins": 50, 278 | "selectLike": 0, 279 | "monthEndAutoCharge": true, 280 | "giveGift": true, 281 | "upLive": "0", 282 | "chargeForLove": "0", 283 | "devicePlatform": "ios", 284 | "coinAddPriority": 1, 285 | "skipDailyTask": true, 286 | "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15" 287 | } 288 | ``` 289 | 290 | **Windows/Linux 用户使用 jar 包时,`release`包中会包含一份`config.json`配置文件,只需将其和`BILIBILI-HELP.jar`放在同一目录即可,执行时优先加载外部配置文件** 291 | 292 | 配置文件参数示意 293 | 294 | | Key | Value | 说明 | 295 | | ------------------ | -------------------- | ------------------------------------------------------------------------ | 296 | | numberOfCoins | [0,5] | 每日投币数量,默认 5 ,为 0 时则不投币 | 297 | | reserveCoins | [0,4000] | 预留的硬币数,当硬币余额小于这个值时,不会进行投币任务,默认值为 50 | 298 | | selectLike | [0,1] | 投币时是否点赞,默认 0, 0:否 1:是 | 299 | | monthEndAutoCharge | [false,true] | 年度大会员月底是否用 B 币券给自己充电,默认 `true`,即充电对象是你本人。 | 300 | | giveGift | [false,true] | 直播送出即将过期的礼物,默认开启,如需关闭请改为 false | 301 | | upLive | [0,送礼 up 主的 uid] | 直播送出即将过期的礼物,指定 up 主,为 0 时则随随机选取一个 up 主 | 302 | | chargeForLove | [0,充电对象的 uid] | 给指定 up 主充电,值为 0 或者充电对象的 uid,默认为 0,即给自己充电。 | 303 | | devicePlatform | [ios,android] | 手机端漫画签到时的平台,建议选择你设备的平台 ,默认 `ios` | 304 | | coinAddPriority | [0,1] | 0:优先给热榜视频投币,1:优先给关注的 up 投币 | 305 | | userAgent | 浏览器 UA | 用户可根据部署平台配置,可根据 userAgent 参数列表自由选取 | 306 | | skipDailyTask | [false,true] | 是否跳过每日任务,默认`true`,如果关闭跳过每日任务,请改为`false` | 307 | 308 | **tips:如果你没有上传过视频并开启充电计划,充电会失败,B 币券会浪费。此时建议配置为给指定的 up 主充电。欢迎给即将秃头的我充电 uid:[14602398][32] ** 309 | 310 | [32]: https://space.bilibili.com/14602398 311 | 312 | userAgent 可选参数列表 313 | 314 | | 平台 | 浏览器 | userAgent | 315 | | --------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------- | 316 | | Windows10 | EDGE(chromium) | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 Edg/86.0.622.69 | 317 | | Windows10 | Chrome | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 | 318 | | masOS | safari | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15 | 319 | | macOS | Firefox | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:65.0) Gecko/20100101 Firefox/65.0 | 320 | | macOS | Chrome | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36 | 321 | 322 | _ps:如果尝试给关注的 up 投币十次后(保不准你关注的是年更 up 主),还没完成每日投币任务,则切换成热榜模式,给热榜视频投币_ 323 | 324 | _投币数量代码做了处理,如果本日投币不能获得经验了,则不会投币,每天只投能获得经验的硬币。假设你设置每日投币 3 个,早上 7 点你自己投了 2 个硬币,则十点半时,程序只会投 1 个)_ 325 | tips:从1.4.1版本开始,随机视频投币有一定的概率会将硬币投给本项目的核心开发者,算是对核心开发者长期以来维护的回馈。 326 | ## 订阅执行结果 327 | 328 | ### Server 酱 Turbo 版 329 | 330 | 目前 Turbo 版本的消息通道支持以下渠道 331 | 332 | - 企业微信应用消息 333 | - Android, 334 | - Bark iOS, 335 | - 企业微信群机器人 336 | - 钉钉群机器人 337 | - 飞书群机器人 338 | - 自定义微信测试号 339 | - 方糖服务号 340 | 341 | 1. 前往 [sct.ftqq.com](https://sct.ftqq.com/sendkey)点击登入,创建账号。 342 | 2. 点击点[SendKey](https://sct.ftqq.com/sendkey) ,生成一个 Key。将其增加到 Github Secrets 中,变量名为 `SERVERPUSHKEY` 343 | 3. [配置消息通道](https://sct.ftqq.com/forward) ,选择方糖服务号,保存即可。 344 | 4. 推送效果展示 345 | ![图示](docs/IMG/wechatMsgPush.png) 346 | 347 | ** 348 | 旧版推送渠道[sc.ftqq.com](http://sc.ftqq.com/9.version0) 即将与 4 月底下线,请前往[sct.ftqq.com](https://sct.ftqq.com/sendkey)生成`Turbo`版本的`Key`,注意,申请 Turbo 版 Key 后请配置消息通道,如果想沿用以前的推送方式,选择方糖服务号即可** 349 | 350 | ### Telegram 订阅执行结果 351 | 352 | 1.在 Telegram 中添加 BotFather 这个账号,然后依次发送/start /newbot 按照提示即可创建一个新的机器人。记下来给你生成的 token。 353 | 354 | 2.搜索刚刚创建的机器人的名字,并给它发送一条消息。 355 | 356 | _特别注意:需要先与机器人之间创建会话,机器人才能下发消息,否则机器人无法主动发送消息,切记!_ 357 | 358 | 3.在 Telegram 中搜索 userinfobot,并给它发送一条消息,它会返回给你 chatid。 359 | 360 | 4.在 Github Secrets 中删除 SERVERPUSHKEY,添加 TELEGRAMBOTTOKEN,TELEGRAMCHATID。 361 | 362 | ### 钉钉机器人 363 | 364 | 1.首先你得有个钉钉企业 [快速注册](https://oa.dingtalk.com/register.html) 365 | 366 | 2.[进入钉钉开放平台添加机器人](https://open-dev.dingtalk.com/#/corprobot) 367 | 368 | 3.添加自定义关键词:BILIBILI 369 | 370 | 4.在 Github Secrets 中的 SERVERPUSHKEY 中更新成机器人的 Webhook 371 | 372 | 例如:`https://oapi.dingtalk.com/robot/send?access_token=XXX` 373 | 374 | 5.完成 375 | 376 | ### PushPlus(Push+) 377 | 378 | 1.[前往 PushPlus 获取 Token](https://www.pushplus.plus/push1.html) 379 | 380 | 2.在 Github Secrets 中的 SERVERPUSHKEY 中更新成获取到的 Token 381 | 382 | 3.完成 383 | 384 | ## 更新和帮助 385 | 386 | ### 使用 Pull APP[推荐] 387 | 388 | 参阅 [Pull APP](https://github.com/apps/pull) 389 | 390 | ### 常见问题解答 391 | 392 | ## 免责声明 393 | 394 | 1. 本工具不会记录你的任何敏感信息,也不会上传到任何服务器上。(例如用户的 cookies 数据,cookies 数据均存在 Actions Secrets 中或者用户自己的设备上) 395 | 2. 本工具不会记录任何执行过程中来自 b 站的数据信息,也不会上传到任何服务器上。(例如 av 号,bv 号,用户 uid 等)。 396 | 3. 本工具执行过程中产生的日志,仅会在使用者自行配置推送渠道后进行推送。日志中不包含任何用户敏感信息。 397 | 4. 如果有人修改了本项目(或者直接使用本项目)盈利恰饭,那和我肯定没关系,我开源的目的单纯是技术分享。 398 | 5. 如果你使用了第三方修改的,打包的本工具代码,那你可得注意了,指不定人就把你的数据上传到他自己的服务器了,这可和我没关系。(**网络安全教育普及任重而道远**) 399 | 6. 本工具源码仅在[JunzhouLiu/BILIBILI-HELPER-PRE](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE)开源,其余的地方的代码均不是我提交的,可能是抄我的,借鉴我的,但绝对不是我发布的,出问题和我也没关系。 400 | 7. 我开源本工具的代码仅仅是技术分享,没有任何丝毫的盈利赚钱目的,如果你非要给我打赏/充电,那我就是网络乞丐,咱们不构成任何雇佣,购买关系的交易。 401 | 8. 本项目不会增加类似于自动转发抽奖,秒杀,下载版权受限视频等侵犯 UP 主/B 站权益的功能,开发这个应用的目的是单纯的技术分享。下游分支开发者/使用者也请不要滥用相关功能。 402 | 9. 本项目欢迎其他开发者参与贡献,基于本工具的二次开发,使用其他语言重写都没有什么问题,能在技术上给你带来帮助和收获就很好. 403 | 10. 本项目遵守[MIT License](https://github.com/JunzhouLiu/BILIBILI-HELPER-PRE/blob/main/LICENSE),请各位知悉。 404 | 405 | ## API 参考列表 406 | 407 | - [SocialSisterYi/bilibili-API-collect](https://github.com/SocialSisterYi/bilibili-API-collect) 408 | - [happy888888/BiliExp](https://github.com/happy888888/BiliExp) 409 | 410 | ## 基于本项目的衍生项目 411 | 412 | - **基于本项目的 docker 封装项目:[SuperNG6/docker-bilibili-helper](https://github.com/SuperNG6/docker-bilibili-helper)** 413 | 414 | - **基于本项目的 docker 镜像:[superng6/bilibili-helper](https://hub.docker.com/r/superng6/bilibili-helper)** 415 | 416 | - **基于本项目的 runer 项目:[KurenaiRyu/bilibili-helper-runer](https://github.com/KurenaiRyu/bilibili-helper-runer)** 417 | 418 | - **基于本项目的 k8s 项目:[yangyang0507/k8s-bilibili-helper](https://github.com/yangyang0507/k8s-bilibili-helper)** 419 | 420 | ## 致谢 421 | 422 | 感谢 JetBrains 对本项目的支持。 423 | 424 | [![JetBrains](docs/IMG/jetbrains.svg)](https://www.jetbrains.com/?from=BILIBILI-HELPER) 425 | 426 | ## License 427 | 428 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FJunzhouLiu%2FBILIBILI-HELPER.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FJunzhouLiu%2FBILIBILI-HELPER?ref=badge_large) 429 | 430 | ## Stargazers over time 431 | 432 | [![Stargazers over time](https://starchart.cc/JunzhouLiu/BILIBILI-HELPER-PRE.svg)](https://starchart.cc/JunzhouLiu/BILIBILI-HELPER-PRE) 433 | --------------------------------------------------------------------------------