From 211c2c65dcc8b22834b1a55db105fb99a3851c5a Mon Sep 17 00:00:00 2001 From: fly Date: Sun, 3 Apr 2022 11:13:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 20 ++++ Note163Checkin.csproj | 15 +++ Note163Checkin.sln | 25 +++++ Program.cs | 185 +++++++++++++++++++++++++++++++++++++ README.md | 56 +++++++++++ 5 files changed, 301 insertions(+) create mode 100644 .github/workflows/main.yml create mode 100644 Note163Checkin.csproj create mode 100644 Note163Checkin.sln create mode 100644 Program.cs create mode 100644 README.md diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..90153f4 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,20 @@ +name: Run + +on: + workflow_dispatch: + repository_dispatch: + +jobs: + build: + + if: github.actor == github.repository_owner + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@main + - name: Run + uses: GabrielBB/xvfb-action@v1 + with: + run: dotnet run + env: + CONF: ${{ secrets.CONF }} \ No newline at end of file diff --git a/Note163Checkin.csproj b/Note163Checkin.csproj new file mode 100644 index 0000000..d1fca16 --- /dev/null +++ b/Note163Checkin.csproj @@ -0,0 +1,15 @@ + + + + Exe + net6.0 + enable + disable + + + + + + + + diff --git a/Note163Checkin.sln b/Note163Checkin.sln new file mode 100644 index 0000000..79159e2 --- /dev/null +++ b/Note163Checkin.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32328.378 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Note163Checkin", "Note163Checkin.csproj", "{A5132704-C07F-4689-8D84-A9F3EBEC6947}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A5132704-C07F-4689-8D84-A9F3EBEC6947}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5132704-C07F-4689-8D84-A9F3EBEC6947}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5132704-C07F-4689-8D84-A9F3EBEC6947}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5132704-C07F-4689-8D84-A9F3EBEC6947}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A5F3DCA6-F4AC-4CE1-B72E-D85664BA280A} + EndGlobalSection +EndGlobal diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..2846c71 --- /dev/null +++ b/Program.cs @@ -0,0 +1,185 @@ +using Newtonsoft.Json.Linq; +using PuppeteerSharp; +using StackExchange.Redis; +using System.Text.Json; + +HttpClient _scClient = null; +Conf _conf = Deserialize(GetEnvValue("CONF")); +if (!string.IsNullOrWhiteSpace(_conf.ScKey)) +{ + _scClient = new HttpClient(); +} + +#region redis + +ConnectionMultiplexer redis = ConnectionMultiplexer.Connect($"{_conf.RdsServer},password={_conf.RdsPwd},name=Note163Checkin,defaultDatabase=0,allowadmin=true,abortConnect=false"); +IDatabase db = redis.GetDatabase(); +bool isRedis = db.IsConnected("test"); +Console.WriteLine("redis:{0}", isRedis ? "有效" : "无效"); + +#endregion + +Console.WriteLine("有道云笔记签到开始运行..."); +for (int i = 0; i < _conf.Users.Length; i++) +{ + User user = _conf.Users[i]; + string title = $"账号 {i + 1}: {user.Task} "; + Console.WriteLine($"共 {_conf.Users.Length} 个账号,正在运行{title}..."); + + #region 获取cookie + + string cookie = string.Empty; + bool isInvalid = true; string result = string.Empty; + + string redisKey = $"Note163_{user.Username}"; + if (isRedis) + { + var redisValue = await db.StringGetAsync(redisKey); + if (redisValue.HasValue) + { + cookie = redisValue.ToString(); + (isInvalid, result) = await IsInvalid(cookie); + Console.WriteLine("redis获取cookie,状态:{0}", isInvalid ? "无效" : "有效"); + } + } + + if (isInvalid) + { + cookie = await Login(user.Username, user.Password); + (isInvalid, result) = await IsInvalid(cookie); + Console.WriteLine("login获取cookie,状态:{0}", isInvalid ? "无效" : "有效"); + if (isInvalid) + {//Cookie失效 + await Notify($"{title}Cookie失效,请检查登录状态!", true); + continue; + } + } + + if (isRedis) + { + Console.WriteLine($"redis更新cookie:{await db.StringSetAsync(redisKey, cookie)}"); + } + + #endregion + + using var client = new HttpClient(); + client.DefaultRequestHeaders.Add("User-Agent", "ynote-android"); + client.DefaultRequestHeaders.Add("Cookie", cookie); + + long space = 0; + space += Deserialize(result).RewardSpace; + + //签到 + result = await (await client.PostAsync("https://note.youdao.com/yws/mapi/user?method=checkin", null)) + .Content.ReadAsStringAsync(); + space += Deserialize(result).Space; + + //看广告 + for (int j = 0; j < 3; j++) + { + result = await (await client.PostAsync("https://note.youdao.com/yws/mapi/user?method=adPrompt", null)) + .Content.ReadAsStringAsync(); + space += Deserialize(result).Space; + } + + //看视频广告 + for (int j = 0; j < 3; j++) + { + result = await (await client.PostAsync("https://note.youdao.com/yws/mapi/user?method=adRandomPrompt", null)) + .Content.ReadAsStringAsync(); + space += Deserialize(result).Space; + } + + await Notify($"有道云笔记{title}签到成功,共获得空间 {space / 1048576} M"); +} +Console.WriteLine("签到运行完毕"); + +async Task<(bool isInvalid, string result)> IsInvalid(string cookie) +{ + using var client = new HttpClient(); + client.DefaultRequestHeaders.Add("User-Agent", "ynote-android"); + client.DefaultRequestHeaders.Add("Cookie", cookie); + //每日打开客户端(即登陆) + string result = await (await client.PostAsync("https://note.youdao.com/yws/api/daupromotion?method=sync", null)) + .Content.ReadAsStringAsync(); + return (result.Contains("error", StringComparison.OrdinalIgnoreCase), result); +} + +async Task Login(string username, string password) +{ + var launchOptions = new LaunchOptions + { + Headless = false, + DefaultViewport = null, + ExecutablePath = @"/usr/bin/google-chrome" + }; + var browser = await Puppeteer.LaunchAsync(launchOptions); + Page page = await browser.DefaultContext.NewPageAsync(); + + await page.GoToAsync("https://note.youdao.com/web", 60_000); + await page.WaitForSelectorAsync(".login-btn", new WaitForSelectorOptions { Visible = true }); + await page.TypeAsync(".login-username", username); + await page.TypeAsync(".login-password", password); + await Task.Delay(5_000); + await page.ClickAsync(".login-btn"); + await page.WaitForSelectorAsync("ydoc-app", new WaitForSelectorOptions { Visible = true }); + + var client = await page.Target.CreateCDPSessionAsync(); + var ckObj = await client.SendAsync("Network.getAllCookies"); + var cks = ckObj.Value("cookies") + .Where(p => p.Value("domain").Contains("note.youdao.com")) + .Select(p => $"{p.Value("name")}={p.Value("value")}"); + + await browser.DisposeAsync(); + return string.Join(';', cks); +} + +async Task Notify(string msg, bool isFailed = false) +{ + Console.WriteLine(msg); + if (_conf.ScType == "Always" || (isFailed && _conf.ScType == "Failed")) + { + await _scClient?.GetAsync($"https://sc.ftqq.com/{_conf.ScKey}.send?text={msg}"); + } +} + +T Deserialize(string json) => JsonSerializer.Deserialize(json, new JsonSerializerOptions +{ + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip +}); + +string GetEnvValue(string key) => Environment.GetEnvironmentVariable(key); + +#region Conf + +class Conf +{ + public User[] Users { get; set; } + public string ScKey { get; set; } + public string ScType { get; set; } + public string RdsServer { get; set; } + public string RdsPwd { get; set; } +} + +class User +{ + public string Task { get; set; } + public string Username { get; set; } + public string Password { get; set; } +} + +#endregion + +class YdNoteRsp +{ + /// + /// Sync奖励空间 + /// + public int RewardSpace { get; set; } + + /// + /// 其他奖励空间 + /// + public int Space { get; set; } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..464a983 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# Note163Checkin + +## 一、Fork 仓库 + +**点击右上角的`Fork`** +![fork](https://img.guoqianfan.com/note/2020/08/fork.png) + +## 二、添加 Secret + +**`Settings`->`Secrets`->`New secret`,添加以下Secret:** +- `Conf`:其值如下: + ```json + { + "Users": [{ + "Task": "CC", //自定义名字,选填 + "Username": "abc@163.com", //账号 + "Password": "aaa" //密码 + }, { + "Task": "MM", + "Username": "123@163.com", + "Password": "111" + } + ], + "ScKey": "", //server酱sckey,不填不开启 + "ScType": "Failed", //通知类型. Always:始终通知; Failed:失败时通知; 不填/其他:不通知; + "RdsServer": "xxx.redislabs.com:1234", //redis地址,选填 + "RdsPwd": "ppp" //redis密码,选填 + } + ``` + `RdsServer`和`RdsPwd`是选填的,用于配置redis,来存储cookie。后续可以重用这个cookie,避免频繁登录账号。建议配置一下,可以使用[redislabs](https://app.redislabs.com/)的免费套餐。 + +**步骤图示如下:** +![添加secret](https://img.guoqianfan.com/note/2020/08/添加secret.png) + +## 三、运行 + +**`Actions`->`Run`->`Run workflow`**: +![run-workflow](https://img.guoqianfan.com/note/2020/08/run-workflow.png) + +**注意**:本项目**不会**自动运行,需要自行在`.github/workflows/main.yml`添加定时任务。 + +## 四、查看运行结果 + +**`Actions`->`Run`->`build`**,能看到下图,表示运行成功 +![查看action运行记录](https://img.guoqianfan.com/note/2020/08/查看action运行记录.png) + +## 注意事项 + +24小时内频繁登录可能会触发验证,程序就会登录失败。此时需要在网页上手动登录一次(需要输入验证码),登录成功后再次运行本程序即可。 + +## 参考 + +参考了以下项目: +- [ydao](https://github.com/yygtboy/ydao/) +- [node-script](https://github.com/SunSeekerX/node-script) +- [youdaoyun](https://github.com/hezhizheng/youdaoyun) \ No newline at end of file