Skip to content

Instantly share code, notes, and snippets.

@f4iey
Last active January 20, 2025 09:43
Show Gist options
  • Save f4iey/70c587d96055f128ee3b1131d963d8d0 to your computer and use it in GitHub Desktop.
Save f4iey/70c587d96055f128ee3b1131d963d8d0 to your computer and use it in GitHub Desktop.
PoC for posting scores on 3830 via DXLog
// Required namespaces
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using HtmlAgilityPack; // Make sure to add HtmlAgilityPack via NuGet
namespace DXLog.net
{
public class ContestSubmission : IScriptClass
{
private HttpClient httpClient;
// Executes at DXLog.net start
public void Initialize(FrmMain main)
{
httpClient = new HttpClient();
}
// Executes as DXLog.net close down
public void Deinitialize()
{
httpClient.Dispose();
}
// Main execution method
public async void Main(FrmMain mainForm, ContestData cdata, COMMain comMain, MidiEvent midiEvent)
{
// Get input variables from DXLog
string email = mainForm.GetVariable("Email");
string callUsed = mainForm.GetVariable("CallUsed");
string callOp = mainForm.GetVariable("CallOp");
string callStation = mainForm.GetVariable("CallStation");
string classType = mainForm.GetVariable("ClassType");
string power = mainForm.GetVariable("Power");
string qth = mainForm.GetVariable("QTH");
string club = mainForm.GetVariable("Club");
string comments = mainForm.GetVariable("Comments");
// Optional fields
bool tworadio = mainForm.GetVariable("TwoRadio") == "True";
bool twobsiq = mainForm.GetVariable("TwoBSIQ") == "True";
bool remoteop = mainForm.GetVariable("RemoteOp") == "True";
// Fetch current contests
var contests = await FetchCurrentContests();
// Example: Submit score for the first contest
if (contests.Count > 0)
{
var firstContest = contests[0];
await SubmitScore(firstContest.Value, email, callUsed, callOp, callStation, classType, power, qth, club, comments, tworadio, twobsiq, remoteop);
}
else
{
mainForm.SetMainStatusText("No contests found.");
}
}
private async Task<List<KeyValuePair<string, string>>> FetchCurrentContests()
{
var contests = new List<KeyValuePair<string, string>>();
var url = "https://www.3830scores.com/contests.php";
var response = await httpClient.GetStringAsync(url);
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(response);
// Find the "Recent Contests" section
var recentContestsSection = htmlDoc.DocumentNode.SelectSingleNode("//div[contains(@class, 'leftcollabel') and text()='Recent Contests']");
if (recentContestsSection != null)
{
var contestsList = recentContestsSection.SelectSingleNode("following-sibling::ul");
if (contestsList != null)
{
foreach (var li in contestsList.SelectNodes("li"))
{
var aTag = li.SelectSingleNode("a");
if (aTag != null)
{
string contestName = aTag.InnerText.Trim();
string submitLink = aTag.GetAttributeValue("href", string.Empty);
contests.Add(new KeyValuePair<string, string>(contestName, submitLink));
}
}
}
}
return contests;
}
private async Task SubmitScore(string submitLink, string email, string callUsed, string callOp, string callStation, string classType, string power, string qth, string club, string comments, bool tworadio, bool twobsiq, bool remoteop)
{
var formData = new Dictionary<string, string>
{
{ "action", "process" },
{ "formaction", "1" },
{ "required_emailadr", email },
{ "required_callused", callUsed },
{ "callop", callOp },
{ "callstation", callStation },
{ "required_class", classType },
{ "required_power", power },
{ "optime", "3" }, // Example value, adjust as needed
{ "qth", qth },
{ "stdclub", club },
{ "clubnotonlist", "" },
{ "subsubtotals[0]", "" },
{ "subsubtotals[1]", "" },
{ "subsubtotals[2]", "" },
{ "subsubtotals[3]", "" },
{ "subsubtotals[4]", "0" },
{ "subsubtotals[5]", "" },
{ "sumsubtot[0]", "0" },
{ "sumsubtot[1]", "0" },
{ "required_totscore_nf", "0" },
{ "comments", comments },
{ "required_emailadr2", email },
{ "required_callused2", callUsed }
};
// Add optional fields if the corresponding boolean is True
if (tworadio)
{
formData["tworadio"] = "+";
}
if (twobsiq)
{
formData["twobsiq"] = "+";
}
if (remoteop)
{
formData["remoteop"] = "+";
}
// Send the POST request
var content = new FormUrlEncodedContent(formData);
var response = await httpClient.PostAsync(submitLink, content);
if (response.IsSuccessStatusCode)
{
// Optionally, you can read the response content if needed
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine("Score submitted successfully!");
}
else
{
string errorResponse = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Failed to submit score: {errorResponse}");
}
}
-- Required libraries
local http = require("socket.http") -- Make sure to have LuaSocket installed
local lxp = require("lxp.lom") -- LuaExpat for XML/HTML parsing
-- Function to fetch current contests
function fetchCurrentContests()
local contests = {}
local url = "https://www.3830scores.com/contests.php"
local response, status = http.request(url)
if status ~= 200 then
return contests -- Return empty if the request failed
end
-- Parse HTML
local html = lxp.lom.parse(response)
local recentContestsSection = findNode(html, "Recent Contests")
if recentContestsSection then
local contestsList = recentContestsSection[1].children[1] -- Assuming it's the first child
for _, li in ipairs(contestsList.children) do
if li.tag == "li" then
local aTag = findNode(li, "a")
if aTag then
local contestName = aTag[1]
local submitLink = aTag.attr.href
table.insert(contests, { name = contestName, value = submitLink })
end
end
end
end
return contests
end
-- Helper function to find a node by text
function findNode(node, text)
for _, child in ipairs(node) do
if child.tag == "div" and child.attr.class and child.attr.class:find("leftcollabel") and child[1] == text then
return child
end
end
return nil
end
-- Function to submit score
function submitScore(submitLink, email, callUsed, callOp, callStation, classType, power, qth, club, comments, tworadio, twobsiq, remoteop)
local formData = {
action = "process",
formaction = "1",
required_emailadr = email,
required_callused = callUsed,
callop = callOp,
callstation = callStation,
required_class = classType,
required_power = power,
optime = "3", -- Example value, adjust as needed
qth = qth,
stdclub = club,
clubnotonlist = "",
subsubtotals = {"", "", "", "", "0", ""}, -- 160, 80, 40, 20, 15, 10
sumsubtot = {"0", "0"}, -- total QSOs, total MULTs
required_totscore_nf = "0",
comments = comments,
required_emailadr2 = email,
required_callused2 = callUsed
}
-- Add optional fields if the corresponding boolean is True
if tworadio then
formData["tworadio"] = "+"
end
if twobsiq then
formData["twobsiq"] = "+"
end
if remoteop then
formData["remoteop"] = "+"
end
-- Prepare form data for POST request
local postData = ""
for key, value in pairs(formData) do
postData = postData .. key .. "=" .. value .. "&"
end
postData = postData:sub(1, -2) -- Remove the last '&'
-- Send the POST request
local response, status = http.request{
url = submitLink,
method = "POST",
headers = {
["Content-Type"] = "application/x-www-form-urlencoded",
["Content-Length"] = tostring(#postData)
},
source = ltn12.source.string(postData)
}
if status == 200 then
print("Score submitted successfully!")
else
print("Failed to submit score: " .. (response or "Unknown error"))
end
end
-- Example usage
local email = "[email protected]"
local callUsed = wtContest:GetMyCallsign()
local callOp = "Operator"
local callStation = "Station"
local classType = "Class"
local power = "Power"
local qth = "QTH"
local club = "Club"
local comments = "Comments"
local tworadio = true
local twobsiq = false
local remoteop = false
-- Fetch contests and submit score for the first contest
local contests = fetchCurrentContests()
if #contests > 0 then
-- prompt use with the list and choose the one
local prompt = ""
for index, value in ipairs(contests) do
prompt = prompt + tostring(index) + ": " + value + "\n"
end
local ctestIndex = wtApp:InputInteger(prompt, "Select 3830 score contest number", 0)
local firstContest = contests[ctestIndex]
submitScore(firstContest.value, email, callUsed, callOp, callStation, classType, power, qth, club, comments, tworadio, twobsiq, remoteop)
else
print("No contests found.")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment