Files
EmoneyFiddlerPlugins/EmoneyInteceptor/Emoney/Controls/EmoneyTabControl.cs
2026-01-07 11:33:05 +08:00

344 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}
}