344 lines
15 KiB
C#
344 lines
15 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|
||
}
|