using Fiddler; using Flurl.Http; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Text.Json.Nodes; using System.Threading.Tasks; using System.Windows.Forms; namespace EmoneyInteceptor.Emoney.Controls { public partial class EmoneyTabControl : UserControl { public EmoneyTabControl() { InitializeComponent(); } public void OnWebSocketMessage(object sender, WebSocketMessageEventArgs args) { if (sender is Session session) { FiddlerApplication.Log.LogString($"Host name:{session.hostname}, host:{session.host}"); } } public void BeforeRequest(Session oSession) { // host 带端口,hostname 不带 if (oSession == null) return; if (!oSession.hostname.ToLower().EndsWith("emoney.cn")) return; //FiddlerApplication.Log.LogString($"Host name:{oSession.hostname}, host:{oSession.host}, url: {oSession.url}"); if (oSession.host.ToLower().Contains("mstat.emoney.cn")) { FiddlerApplication.Log.LogString($"mstat request uri: {oSession.url}"); if (oSession.uriContains("/UserAnalysis/upload")) { // 清除信息,不追踪 JObject jo = JObject.Parse(oSession.GetRequestBodyAsString()); jo["events"] = new JArray(); jo["pages"] = new JArray(); FiddlerApplication.Log.LogString("Clean UserAnalysis from Emony"); oSession.RequestBody = Encoding.UTF8.GetBytes(jo.ToString(Newtonsoft.Json.Formatting.None)); } } } public void BeforeResponse(Session oSession) { int pid = 888020000; if (oSession == null) return; if (!oSession.hostname.ToLower().EndsWith("emoney.cn") && !oSession.hostname.ToLower().EndsWith("aliyuncs.com")) return; if (oSession.RequestHeaders.Exists("Authorization")) { if (txtAuth.Text != oSession.RequestHeaders["Authorization"]) txtAuth.Text = oSession.RequestHeaders["Authorization"]; } if (oSession.uriContains("/user/auth/login")) { return; // 用户信息 inject if (!oSession.ResponseHeaders.ExistsAndContains("Content-Type", "application/json")) return; Debug.Write(oSession.GetResponseBodyAsString()); JObject jo = JObject.Parse(oSession.GetResponseBodyAsString()); jo["detail"]["pc"]["isMaster"] = true; jo["detail"]["pc"]["isZy"] = true; jo["detail"]["pc"]["pid"] = pid; oSession.ResponseBody = Encoding.UTF8.GetBytes(jo.ToString(Newtonsoft.Json.Formatting.None)); } if (oSession.uriContains("/user/info")) { // 用户信息 inject if (!oSession.ResponseHeaders.ExistsAndContains("Content-Type", "application/json")) return; Debug.Write(oSession.GetResponseBodyAsString()); JObject jo = JObject.Parse(oSession.GetResponseBodyAsString()); jo["detail"]["pc"]["isMaster"] = true; jo["detail"]["pc"]["isZy"] = true; jo["detail"]["pc"]["pid"] = pid; jo["detail"]["login"]["loginType"] = 1; jo["detail"]["isShareSucc"] = true; jo["detail"]["enableLiveShare"] = true; oSession.ResponseBody = Encoding.UTF8.GetBytes(jo.ToString(Newtonsoft.Json.Formatting.None)); } if (oSession.uriContains("/kankan/kankan/LoginKanKan")) { // 用户信息 inject if (!oSession.ResponseHeaders.ExistsAndContains("Content-Type", "application/json")) return; Debug.Write(oSession.GetResponseBodyAsString()); JObject jo = JObject.Parse(oSession.GetResponseBodyAsString()); jo["detail"]["pid"] = pid; oSession.ResponseBody = Encoding.UTF8.GetBytes(jo.ToString(Newtonsoft.Json.Formatting.None)); } if (oSession.uriContains("/Config/UserExtendInfo/Index")) { return; // 额外信息 inject if (!oSession.ResponseHeaders.ExistsAndContains("Content-Type", "application/json")) return; Debug.Write(oSession.GetResponseBodyAsString()); JObject jo = JObject.Parse(oSession.GetResponseBodyAsString()); jo["detail"]["product"]["id"] = pid; oSession.ResponseBody = Encoding.UTF8.GetBytes(jo.ToString(Newtonsoft.Json.Formatting.None)); } if (oSession.uriContains("/user/authority/isshareenable")) { return; if (!oSession.ResponseHeaders.ExistsAndContains("Content-Type", "application/json")) return; Debug.Write(oSession.GetResponseBodyAsString()); JObject jo = JObject.Parse(oSession.GetResponseBodyAsString()); jo["detail"] = true; oSession.ResponseBody = Encoding.UTF8.GetBytes(jo.ToString(Newtonsoft.Json.Formatting.None)); } if (oSession.uriContains("v1000003/config_ind_online.json") && oSession.HTTPMethodIs("GET")) { if (File.Exists("config_ind_online.json")) { string configFile = File.ReadAllText("config_ind_online.json"); oSession.ResponseBody = Encoding.UTF8.GetBytes(configFile); } } if (oSession.uriContains("/Strategy/PickStock/Filter")) { // 选股过滤器默认规则拦截,将 isLocked 由 true 改为 false if (!oSession.ResponseHeaders.ExistsAndContains("Content-Type", "application/json")) return; Debug.WriteLine(oSession.GetResponseBodyAsString()); JObject jo = JObject.Parse(oSession.GetResponseBodyAsString()); // 三个层级 // 顶层 detail.isLocked // 子层 detail.subFilters[].isLocked // 孙层 detail.subFilters[].....fields[].isLocked // 如果 subFilters 还有 subFilters 就继续遍历 void UnlockFilter(JToken subFilter) { if (subFilter == null) return; // 当前层级有 isLocked 则清除 // 适用于顶级 detail、每层 subFilter、孙子节点 field subFilter.SelectToken("isLocked")?.Replace(false); var subFilters = subFilter.SelectToken("subFilters"); if (subFilters != null) { // 当前层 subFilters 不为 null,则对其中每个 subFilter 都处理 foreach(var _subFilter in subFilters) { UnlockFilter(_subFilter); } } else { // 当前层已不含 subFilters,则应该存在 fields var fields = subFilter?.SelectToken("fields"); if (fields != null) { foreach (var field in fields) { field?.SelectToken("isLocked")?.Replace(false); } } } } string[] strategyCodes = { "strategy_intersection_more", // 策略叠加内的策略叠加 "super_selection_cldj" // 超级选股内的策略叠加 }; void AddStrategyIntersection(JToken subFilter) { if (subFilter == null) return; // 当前 token 是空直接返回 JArray subFilters = subFilter.SelectToken("subFilters")?.Value(); if (strategyCodes.Contains(subFilter["code"].Value())) { // 到相应层级,如果原本没有的就覆盖添加相应的内容 if (subFilters == null) { subFilters = new JArray(); subFilter["subFilters"] = subFilters; } // 载入我们自己准备好的策略权限 if (!File.Exists("prepared_strategies.json")) return; string str = File.ReadAllText("prepared_strategies.json"); JArray jArray = JArray.Parse(str); foreach (JToken strategy in jArray) { if (!subFilters.Any(f => f["code"].Value() == strategy["code"].Value())) { subFilters.Add(strategy); } } return; } if (subFilters == null) return; foreach (var _subFilter in subFilters) { AddStrategyIntersection(_subFilter); } } // detail 本身就是一个 filter UnlockFilter(jo.SelectToken("detail")); AddStrategyIntersection(jo.SelectToken("detail")); oSession.ResponseBody = Encoding.UTF8.GetBytes(jo.ToString(Newtonsoft.Json.Formatting.None)); } if (oSession.uriContains("/user/Authority/GetMyAuthority")) { if (!oSession.ResponseHeaders.ExistsAndContains("Content-Type", "application/json")) return; Debug.WriteLine(oSession.GetResponseBodyAsString()); JObject jo = JObject.Parse(oSession.GetResponseBodyAsString()); // 添加快速选股权限,该权限未在 config_ind_online.json 中体现 // 是在分析网页选股的时候发现的 var stockPickingAuthorities = new Dictionary { { "kuaisuxuangu", "快速选股" }, { "zonghexuangu", "综合选股" }, { "celvediejia", "策略叠加" } }; foreach (var stockPickingAuthority in stockPickingAuthorities) { jo["detail"].Value().Add(new JObject { ["code"] = stockPickingAuthority.Key, ["name"] = stockPickingAuthority.Value, ["isValid"] = true, ["isFee"] = true, ["startTime"] = 1688486400000, ["endTime"] = 7258089599000, ["needPermission"] = true, ["groupId"] = 9, ["groupName"] = "选股" }); } if (File.Exists("config_ind_online.json")) { string configFile = File.ReadAllText("config_ind_online.json"); JObject indConfig = JObject.Parse(configFile); JObject indIdNameConfig = indConfig["indIdNameConfig"].Value(); JObject indMap = indConfig["indMap"].Value(); JArray indGruopList = indConfig["indGroupList"].Value(); foreach (var pair in indMap) { string indId = pair.Key; JObject ind = pair.Value as JObject; if (ind["isCalc"].Value() == 1) continue; if (ind["noPermShow"] == null || ind["noPermShow"].Value()) continue; string permCode = ind["permCode"].Value(); string groupId = "10"; string groupName = "决策"; // 只专注于在线隐藏指标 if (jo["detail"].Any(o => o["code"].Value() == permCode)) { Debug.WriteLine($"已有 {permCode} 不覆盖"); continue; } jo["detail"].Value().Add(new JObject { ["code"] = permCode, ["name"] = indIdNameConfig[indId].Value(), ["isValid"] = true, ["isFee"] = true, ["startTime"] = 1688486400000, ["endTime"] = 7258089599000, ["needPermission"] = true, ["groupId"] = int.Parse(groupId), ["groupName"] = groupName }); } } oSession.ResponseBody = Encoding.UTF8.GetBytes(jo.ToString(Newtonsoft.Json.Formatting.None)); } } private async void btnEncode_Click(object sender, EventArgs e) { if (txtProtocolID.Text.Trim() == string.Empty) { MessageBox.Show("ProtocolID 不能为空!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } int protocolId; try { protocolId = Convert.ToInt32(txtProtocolID.Text.Trim()); } catch { MessageBox.Show("ProtocolID 只能为数字!", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } var url = "http://localhost:9999/api/v1/emoney/converter/request/encode"; try { string r = await url.PostJsonAsync(new { protocolId, protocolBody = txtRequestJson.Text, }).ReceiveString(); if (r != null) { JObject jo = JObject.Parse(r); if (jo["code"].Value() == 0) { txtProtobufBody.Text = jo["result"].Value(); } else { MessageBox.Show(jo["msg"].Value(), "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } catch (Exception ex) { FiddlerApplication.Log.LogString($"转换 JSON 为 Protobuf 请求出现错误:{ex.Message}"); FiddlerApplication.Log.LogString(ex.StackTrace); MessageBox.Show($"转换 JSON 为 Protobuf 请求出现错误:{ex.Message}", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } }