3.5.2 commit

This commit is contained in:
Doug Macintosh
2026-03-08 16:20:06 -04:00
parent a59a047cd1
commit bfe37f6426
26 changed files with 1998 additions and 102714 deletions

465
MegaT.cs
View File

@@ -5,17 +5,13 @@ using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Reflection;
namespace bdf
{
public class MegaT
{
// SIP Extractions section
// A class to hold the extracted data for a single row of SIP INSIS data
public class SIPData
// A class to hold the extracted data for a single row of INSIS data
public class INSISData
{
public string Item { get; set; }
public string Code { get; set; }
@@ -35,13 +31,12 @@ namespace bdf
}
}
public static List<SIPData> GetSIPDetails_bySCHED(string sched, ref CookieContainer cookies)
public static List<INSISData> GetRVNUDetails_bySCHED(string sched, ref CookieContainer cookies)
{
bool success = false;
byte[] webResp = null;
List<SIPData> sip_rvnu = new List<SIPData> { };
List<INSISData> rvnu_items = new List<INSISData> { };
success = Web.MakeRequest(
"GET",
@@ -53,7 +48,7 @@ namespace bdf
if (!success)
{
Logger.Log(5, "Get SIP INSIS TopLevel Details FAIL {0}", sched);
Logger.Log(5, "Get INSIS RVNU TopLevel Details FAIL {0}", sched);
}
string html = Encoding.ASCII.GetString(webResp);
@@ -63,16 +58,16 @@ namespace bdf
{
try
{
Dictionary<string, string> rvnu = SIP_RVNU_items(html);
HashSet<string > rvnu_links = RVNU_item_links(html);
foreach (var kvp in rvnu)
foreach (var link in rvnu_links)
{
success = false;
webResp = null;
success = Web.MakeRequest(
"GET",
"http://megatool.rogers.com/megatool/megatool/INSIS/" + kvp.Value.ToString(),
"http://megatool.rogers.com/megatool/megatool/INSIS/" + link.ToString(),
false,
"",
ref webResp,
@@ -80,13 +75,14 @@ namespace bdf
if (!success)
{
Logger.Log(5, "Get INSIS RVNU Details FAIL {0}-{1} {2}", sched, kvp.Key, webResp);
Logger.Log(5, "Get INSIS RVNU Details FAIL {0}-{1} {2}", sched, link, webResp);
}
string html2 = Encoding.ASCII.GetString(webResp);
SIPData x = ExtractSIPData(html2);
sip_rvnu.Add(x);
Logger.Log(6, "Desc: {0} sip_rnvu size {1}", x.Description, sip_rvnu.Count);
INSISData x = ExtractINSISData(html2);
if (!x.BillingType.Contains("Delete")) // Feb 12 2026
rvnu_items.Add(x);
Logger.Log(6, "Desc: {0} | rvnu_items size={1}", x.Description, rvnu_items.Count);
}
@@ -96,12 +92,12 @@ namespace bdf
Logger.Log(0, "Error SCHED {0} - SIP HTML={0}", sched, e.Message);
}
}
return sip_rvnu;
return rvnu_items;
}
public static Dictionary<string, string> SIP_RVNU_items(string html)
public static HashSet<string> RVNU_item_links(string html)
{
Dictionary<string, string> result = new System.Collections.Generic.Dictionary<string, string>( System.StringComparer.OrdinalIgnoreCase);
HashSet<string> result = new HashSet<string>(System.StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrWhiteSpace(html))
return result;
@@ -132,27 +128,9 @@ namespace bdf
if (tdMatches.Count < 5)
continue;
// ----- Extract Service Code (5th <td>) -----
string serviceCode =
System.Text.RegularExpressions.Regex.Replace(
tdMatches[4].Groups[1].Value,
"<[^>]+>",
string.Empty
).Trim();
if (serviceCode.Length == 0)
continue;
// Skip "Install" rows
if (serviceCode.IndexOf("Install", System.StringComparison.OrdinalIgnoreCase) >= 0)
{
//Console.WriteLine("skipping [{0}]", serviceCode);
continue;
}
// ----- Extract openIt('...') from 4th <td> -----
string fourthTd = tdMatches[3].Groups[1].Value;
//Console.WriteLine("4th [{0}]", fourthTd);
//Logger.Log(6,"4th [{0}]", fourthTd);
string openItValue = "";
if (fourthTd.IndexOf("RVNU", System.StringComparison.OrdinalIgnoreCase) >= 0)
@@ -167,36 +145,31 @@ namespace bdf
if (!onclickMatch.Success)
{
//Console.WriteLine("no match");
Logger.Log(3,"no rvnu match");
continue;
}
openItValue = onclickMatch.Groups["openit"].Value.Trim();
}
else // Skip non-RVNU rows
{
//Console.WriteLine("non-revenue [{0}]", fourthTd);
Logger.Log(4,"non-revenue [{0}]", fourthTd);
continue;
}
// Avoid duplicate keys
if (!result.ContainsKey(serviceCode))
{
result.Add(serviceCode, openItValue);
}
result.Add(openItValue);
}
return result;
}
public static SIPData ExtractSIPData(string html)
public static INSISData ExtractINSISData(string html)
{
var extractedData = new SIPData();
var extractedData = new INSISData();
// Regex to find table rows (<tr>) within a specific table structure (adjust if needed, e.g., using a table ID)
// The pattern uses capturing groups for each <td> content
// It assumes <td> tags might have extra spaces or attributes, but the content inside is the target.
Dictionary<string, string> result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
if (string.IsNullOrWhiteSpace(html))
@@ -220,17 +193,17 @@ namespace bdf
if (!result.ContainsKey(heading))
{
result.Add(heading.Replace(" ", ""), value); //Remove field label spaces to make them valid dictionary entries
Logger.Log(3, "Found: {0}={1}", heading, value);
Logger.Log(5, "Found: {0}={1}", heading, value);
}
}
System.Type type = typeof(SIPData);
System.Type type = typeof(INSISData);
foreach (System.Reflection.PropertyInfo prop in type.GetProperties(
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.Public))
{
//Console.WriteLine("a4n {0} : {1}", prop.PropertyType.Name, prop.Name);
Logger.Log(6,"a4n {0} : {1}", prop.PropertyType.Name, prop.Name);
if (prop.PropertyType != typeof(string))
continue;
@@ -240,7 +213,7 @@ namespace bdf
if (result.TryGetValue(prop.Name, out string value))
{
//Console.WriteLine("a5 {0}", value);
Logger.Log(6,"a5 {0}", value);
try
{
prop.SetValue( extractedData, (string)value);
@@ -254,6 +227,7 @@ namespace bdf
return extractedData;
}
// Takes MM-d-yyyy and converts to yyyyMMdd
public static string ConvertDate(string input)
{
System.DateTime date =
@@ -267,291 +241,7 @@ namespace bdf
}
//------------------------------------
/*
public static Dictionary<string, string> ExtractServiceCodesFromTable(string html)
{
Dictionary<string, string> result = new Dictionary<string, string>();
if (string.IsNullOrWhiteSpace(html))
return result;
// Match each <tr> with id="R<number>", including multi-line content
string trPattern = @"<tr[^>]*id\s*=\s*['""]R\d+['""][^>]*>(.*?)</tr>";
var trMatches = Regex.Matches(
html,
trPattern,
RegexOptions.IgnoreCase | RegexOptions.Singleline // Singleline allows . to match newlines
);
foreach (Match trMatch in trMatches)
{
string trContent = trMatch.Groups[1].Value;
// Match all <td> elements inside the <tr>
var tdMatches = Regex.Matches(
trContent,
@"<td[^>]*>(.*?)</td>",
RegexOptions.IgnoreCase | RegexOptions.Singleline
);
if (tdMatches.Count >= 4)
{
string fourthTd = tdMatches[3].Groups[1].Value;
// Extract openIt('...') string inside the <a> tag
string onclickPattern = @"openIt\('(?<openit>[^']+)'\)";
var onclickMatch = Regex.Match(fourthTd, onclickPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
if (onclickMatch.Success)
{
string openItValue = onclickMatch.Groups["openit"].Value.Trim();
//result.Add(openItValue);
Console.WriteLine("Found {0}", openItValue);
}
}
}
return result;
}
/*
// Match each <tr> row
string rowPattern = @"<tr[^>]*id\s*=\s*['""]R\d+['""][^>]*>(.*?)</tr>";
MatchCollection rowMatches = Regex.Matches(html, rowPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
foreach (Match rowMatch in rowMatches)
{
string rowContent = rowMatch.Groups[1].Value;
Console.WriteLine("<TR> {0}", rowContent);
foreach (Match trMatch in rowContent)
{
string trContent = trMatch.Groups[1].Value;
Console.WriteLine("<TR> {0}", trContent);
// Match all <td> elements inside the <tr>
var tdMatches = Regex.Matches(
trContent,
@"<td[^>]*>(.*?)</td>",
RegexOptions.IgnoreCase | RegexOptions.Singleline
);
if (tdMatches.Count >= 4)
{
string fourthTd = tdMatches[3].Groups[1].Value;
// Extract openIt('...') string inside the <a> tag
string onclickPattern = @"openIt\('(?<openit>[^']+)'\)";
var onclickMatch = Regex.Match(fourthTd, onclickPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
if (onclickMatch.Success)
{
string openItValue = onclickMatch.Groups["openit"].Value.Trim();
result.Add(openItValue);
}
}
}
// Match <a ... onclick='...' ...> followed by <td> with service code
string cellPattern =
@"<a[^>]*onclick\s*=\s*['""][^'""]*'(?<onclick>[^']+)'[^'""]*['""][^>]*>.*?</a>.*?" +
@"<td[^>]*>\s*(?<code>[^<\s][^<]*)\s*</td>";
Match cellMatch = Regex.Match(rowContent, cellPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
if (cellMatch.Success)
{
string onclick = cellMatch.Groups["onclick"].Value.Trim();
string code = cellMatch.Groups["code"].Value.Trim();
if (!string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(onclick))
{
// Avoid duplicate keys
if (!result.ContainsKey(code))
result[code] = onclick;
Console.WriteLine("Found {0} : {1}", code, onclick);
}
}
}
return result;
*/
// from Megatool RVNU sub-page, return extracted SIPData item
// old
public static List<SIPData> ExtractSIPData_RevScreen(string htmlContent)
{
var extractedData = new List<SIPData>();
// Regex to find table rows (<tr>) within a specific table structure (adjust if needed, e.g., using a table ID)
// The pattern uses capturing groups for each <td> content
// It assumes <td> tags might have extra spaces or attributes, but the content inside is the target.
string rowPattern = @"<tr\s*[^>]*?\bid=[^>]*?>(.*?)</tr\s*>"; //@"<tr\s*[^>]*>(.*?)</tr\s*>";
string cellPattern = @"<td\s*[^>]*>[$]*(.*?)</td>";
// Find all rows in the HTML
MatchCollection rows = Regex.Matches(htmlContent, rowPattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
foreach (Match rowMatch in rows)
{
string rowHtml = rowMatch.Groups[1].Value;
// Find all cells in the current row
MatchCollection cells = Regex.Matches(rowHtml, cellPattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
// Check if the row has exactly 9 columns of data (Item, Description, Unit Price, Qty, Amount)
if (cells.Count >= 9)
{
// Extract the inner text and clean up whitespace
string item = cells[0].Groups[1].Value.Replace("&nbsp;", " ").Trim();
string code = cells[1].Groups[1].Value.Replace("&nbsp;", " ").Trim();
string description = cells[2].Groups[1].Value.Replace("&nbsp;", " ").Trim();
string period = cells[3].Groups[1].Value.Replace("&nbsp;", " ").Trim();
string unitPrice = cells[4].Groups[1].Value.Replace("&nbsp;", " ").Trim();
string qty = cells[5].Groups[1].Value.Replace("&nbsp;", " ").Trim();
string amount = cells[6].Groups[1].Value.Replace("&nbsp;", " ").Trim();
string billingType = cells[7].Groups[1].Value.Replace("&nbsp;", " ").Trim();
string currency = cells[8].Groups[1].Value.Replace("&nbsp;", " ").Trim();
// Add to the list
extractedData.Add(new SIPData
{
Item = item,
Code = code,
Description = description,
Period = period,
UnitPrice = unitPrice,
Quantity = qty,
Amount = amount,
BillingType = billingType,
Currency = currency
});
}
}
return extractedData;
}
//old version
public static double getSIP_RVNU(string sched, ref CookieContainer cookies)
{
bool success = false;
double mrr = 0;
byte[] webResp = null;
success = Web.MakeRequest(
"GET",
"http://megatool.rogers.com/megatool/megatool/INSIS/insisMainwindow.asp?serv_id=" + sched,
false,
"",
ref webResp,
ref cookies);
if (!success)
{
Logger.Log(5, "MRR FAIL {0}", sched);
}
string html = Encoding.ASCII.GetString(webResp);
if (html.Length > 5000)
{
try
{
Regex word = new Regex(@"<tr\b[^>]*>[\s\S]*?Total\s+MRR[\s\S]*?\$\s*([0-9]{1,3}(?:,[0-9]{3})*(?:\.[0-9]{2})?)[\s\S]*?<\/tr>"); // find MRR dollar value
Match m = word.Match(html); //
if (m.Success)
{
Group g = m.Groups[1];
//Capture c = g.Captures[0];
Logger.Log(6, "Regex Match {0} / Group Count {1} / Captures Count {2} / result={3}", m.Success, m.Groups.Count, m.Captures.Count, g.Value);
mrr = Convert.ToDouble(g.Value);
if ((mrr >= 0) && (mrr <= 999999))
Logger.Log(5, "MRR found successfully {0}", mrr);
}
else
{
Logger.Log(1, "No MRR found.");
}
}
catch (Exception e)
{
Logger.Log(0, "Error parsing MRR {0}", e.Message);
}
}
return mrr;
}
// _RevScreen
public static List<SIPData> GetSIPDetails(string sched, ref CookieContainer cookies)
{
string token = "";
bool success = false;
byte[] webResp = null;
string html = "";
// http://megatool.rogers.com/megatool/megatool/INSIS/revenue.asp?logo=N&serv_id==
if (sched != "dummy")
{
success = Web.MakeRequest(
"GET",
"http://megatool.rogers.com/megatool/megatool/INSIS/revenue.asp?logo=N&serv_id=" + sched,
false,
"",
ref webResp,
ref cookies);
if (!success)
{
Logger.Log(0, "Get SIP details fail{0}", token);
}
//Console.WriteLine(Encoding.ASCII.GetString(webResp));
html = Encoding.ASCII.GetString(webResp);
}
else
{
success = true;
html = "";
}
List<SIPData> data = new List<SIPData> { };
// SIP details extraction
try
{
// data = ExtractSIPData(html);
SIPData junk = ExtractSIPData(html);
/*
foreach (var row in data)
{
//Console.WriteLine(row.ToString());
if ( row.Description.Contains("Access") ) Logger.Log(1,"Found Access: {0} x {1}", row.UnitPrice, row.Qty);
if ( row.Description.Contains("Session") ) Logger.Log(1, "Found Sessions: {0} x {1}", row.UnitPrice, row.Qty);
}
*/
}
catch (Exception e)
{
Logger.Log(0, "Exception: SIP Details {0}:{1}", e.Message, e.InnerException.Message);
}
return data; // no match found
}
//------------------------------------
// SCHED-A Info Extraction
// SCHED-A Info Extraction from "Schedule Details" Megatool page, most import is SO<->SCHED mapping
public static Dictionary<string, string> GetParms(string sched, ref CookieContainer cookies)
{
string token = "";
@@ -561,7 +251,8 @@ namespace bdf
Dictionary<string, string> parms = new Dictionary<string, string> { };
if (sched != "dummy")
//if (sched != "dummy")
if ( sched.Length != 3 ) // not a 'dummy' request
{
success = Web.MakeRequest(
"GET",
@@ -586,7 +277,7 @@ namespace bdf
html = "";
}
//following builds the columns in order, one IF per column
try
{
// SO + up to 5 non-digits + six digits
@@ -746,63 +437,19 @@ namespace bdf
{
Logger.Log(0, "Exception: {0}:{1}", e.Message, e.InnerException.Message);
}
if (sched.ToUpper() == "SIP") // add SIP extension columns to Megatool table to track Sessions and Access costs
{
parms.Add("AQty", "XXX");
parms.Add("ACst", "XXX");
parms.Add("SQty", "XXX");
parms.Add("SCst", "XXX");
}
return parms; // no match found
}
// <tr\b[^>]*>[\s\S]*?MRR[\s\S]*?\$\s*(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)[\s\S]*?</tr>
public static double getMRR(string sched, ref CookieContainer cookies)
{
bool success = false;
double mrr = 0;
byte[] webResp = null;
success = Web.MakeRequest(
"GET",
"http://megatool.rogers.com/megatool/megatool/INSIS/insisMainwindow.asp?serv_id=" + sched,
false,
"",
ref webResp,
ref cookies);
if (!success)
{
Logger.Log(5, "MRR FAIL {0}", sched);
}
string html = Encoding.ASCII.GetString(webResp);
if (html.Length > 5000)
{
try
{
Regex word = new Regex(@"<tr\b[^>]*>[\s\S]*?Total\s+MRR[\s\S]*?\$\s*([0-9]{1,3}(?:,[0-9]{3})*(?:\.[0-9]{2})?)[\s\S]*?<\/tr>"); // find MRR dollar value
Match m = word.Match(html); //
if (m.Success)
{
Group g = m.Groups[1];
//Capture c = g.Captures[0];
Logger.Log(6, "Regex Match {0} / Group Count {1} / Captures Count {2} / result={3}", m.Success, m.Groups.Count, m.Captures.Count, g.Value);
mrr = Convert.ToDouble(g.Value);
if ((mrr >= 0) && (mrr <= 999999))
Logger.Log(5, " {1} MRR found successfully {0}", mrr, sched);
}
else
{
Logger.Log(1, "No MRR found.");
}
}
catch (Exception e)
{
Logger.Log(0, "Error parsing MRR {0}", e.Message);
}
}
return mrr;
}
// Deal with filtering out which SCHEDs are actually active with a given prefix
public static bool SchedXL(string sched, ref CookieContainer cookies)
{
string token = "";
@@ -906,7 +553,9 @@ namespace bdf
break;
string value = html.Substring(start, end - start);
scheds.Add(value);
if ( !scheds.Exists(s => s == value) ) // skip any duplicate SCHEDA entries, such as with WAVs
scheds.Add(value);
index = end + endKey.Length;
}
@@ -921,22 +570,27 @@ namespace bdf
bool success = false;
// Explicit credentials (domain\user)
var creds = new NetworkCredential(user, pass, "RCI");
// var creds = new NetworkCredential(user, pass, "RCI");
// Add to global cred cache for NTLM auth - Needed to survive Windows 11 / .NET481. Works fine in Mono or with Fiddler, but not straight .exe
bdf.cache.Add(new Uri(url), "NTLM", CredentialCache.DefaultNetworkCredentials);
// A cookie container is REQUIRED to capture session cookies
// var cookies = new CookieContainer();
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
request.AllowAutoRedirect = false; // Required for NTLM handshake
request.PreAuthenticate = false; // NTLM cannot pre-authenticate
request.PreAuthenticate = true;
//request.PreAuthenticate = false; // NTLM cannot pre-authenticate
request.UseDefaultCredentials = false;
request.Credentials = creds; // Enables SSPI NTLM handshake
request.Credentials = bdf.cache;
//request.Credentials = creds; // Enables SSPI NTLM handshake
request.CookieContainer = cookies; // Store session cookies
request.KeepAlive = true; // NTLM requires same connection
//request.UnsafeAuthenticatedConnectionSharing = true;
request.UserAgent = "NTLMClient/.NET4.8";
Logger.Log(1, "Megatool NTLM authentication for ({0})...", user);
Logger.Log(1, "Megatool authentication for ({0})...", user);
HttpWebResponse response;
@@ -963,11 +617,7 @@ namespace bdf
{
body = reader.ReadToEnd();
}
/*
Console.WriteLine("\n--- Page Content ---\n");
Console.WriteLine(body);
Console.WriteLine("\n--- Cookies Received ---");
*/
foreach (Cookie ck in cookies.GetCookies(request.RequestUri))
{
Logger.Log(5, $"{ck.Name} = {ck.Value}; Domain={ck.Domain}; Path={ck.Path}");
@@ -978,7 +628,6 @@ namespace bdf
return success;
}
}
}
}