first commit

This commit is contained in:
2026-01-07 11:33:05 +08:00
commit fc54ffd43b
215 changed files with 31856 additions and 0 deletions

View File

@@ -0,0 +1,343 @@
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<JArray>();
if (strategyCodes.Contains(subFilter["code"].Value<string>()))
{
// 到相应层级,如果原本没有的就覆盖添加相应的内容
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<string>() == strategy["code"].Value<string>()))
{
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<string, string>
{
{ "kuaisuxuangu", "快速选股" },
{ "zonghexuangu", "综合选股" },
{ "celvediejia", "策略叠加" }
};
foreach (var stockPickingAuthority in stockPickingAuthorities)
{
jo["detail"].Value<JArray>().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>();
JObject indMap = indConfig["indMap"].Value<JObject>();
JArray indGruopList = indConfig["indGroupList"].Value<JArray>();
foreach (var pair in indMap)
{
string indId = pair.Key;
JObject ind = pair.Value as JObject;
if (ind["isCalc"].Value<int>() == 1) continue;
if (ind["noPermShow"] == null || ind["noPermShow"].Value<bool>()) continue;
string permCode = ind["permCode"].Value<string>();
string groupId = "10";
string groupName = "决策";
// 只专注于在线隐藏指标
if (jo["detail"].Any(o => o["code"].Value<string>() == permCode))
{
Debug.WriteLine($"已有 {permCode} 不覆盖");
continue;
}
jo["detail"].Value<JArray>().Add(new JObject
{
["code"] = permCode,
["name"] = indIdNameConfig[indId].Value<string>(),
["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<int>() == 0)
{
txtProtobufBody.Text = jo["result"].Value<string>();
}
else
{
MessageBox.Show(jo["msg"].Value<string>(), "警告", 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);
}
}
}
}