Compare commits

...

7 Commits

Author SHA1 Message Date
duffyduck f0bcfdfd30 Release v0.0.0.12 2026-04-03 12:55:53 +02:00
duffyduck 650d0def51 Fix Starface contact field mapping based on actual API response
Read mapping: Match by 'name' field instead of 'displayKey' because
many fields use USER_DEFINED as displayKey (street, city, state,
comment). Actual name fields: firstname, familyname, company, phone,
mobile, homephone, fax, e-mail, url, comment, street, city, postcode,
state, country.

Write mapping: Use correct 4-block structure (contact, address,
telephone, email) with proper resourceKey values matching the
Starface internal format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:54:47 +02:00
duffyduck f57518a1d5 Release v0.0.0.11 2026-04-03 12:51:14 +02:00
duffyduck 1be0a94b51 Fetch full contact details from Starface instead of summary
The contacts list endpoint only returns summaryValues/phoneNumbers.
Now fetch each contact individually via GET /contacts/{id} to get
all fields (blocks/attributes). Also log the detail JSON structure
so we can verify the field mapping is correct.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:50:16 +02:00
duffyduck 3c38a1a6cc Release v0.0.0.10 2026-04-03 12:17:30 +02:00
duffyduck ba0b79de64 Release v0.0.0.9 2026-04-03 12:14:42 +02:00
duffyduck 60bd3163a9 Add debug logging for raw Starface API contact data
Logs the first contact from Starface API response into the sync
log so we can see the actual JSON structure and fix field mapping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 12:13:43 +02:00
5 changed files with 138 additions and 61 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
; Erfordert Inno Setup 6.x (https://jrsoftware.org/isinfo.php)
#define MyAppName "Starface Outlook Sync"
#define MyAppVersion "0.0.0.8"
#define MyAppVersion "0.0.0.12"
#define MyAppPublisher "HackerSoft - Hacker-Net Telekommunikation"
#define MyAppURL "https://www.hacker-net.de"
#define MyAppExeName "StarfaceOutlookSync.exe"
@@ -155,11 +155,14 @@ namespace StarfaceOutlookSync.Services
return books;
}
public event Action<string> OnDebug;
public async Task<List<UnifiedContact>> GetContactsAsync(StarfaceAddressBook book)
{
var contacts = new List<UnifiedContact>();
int page = 0;
const int pageSize = 200;
bool firstPage = true;
while (true)
{
@@ -206,8 +209,34 @@ namespace StarfaceOutlookSync.Services
if (array.Count == 0) break;
OnDebug?.Invoke($"Seite {page}: {array.Count} Kontakte in Liste");
// Die Listen-API gibt nur Summary zurueck.
// Jeden Kontakt einzeln abrufen fuer alle Felder.
foreach (var item in array)
contacts.Add(MapFromStarface(item));
{
var id = item["id"]?.ToString();
if (string.IsNullOrEmpty(id)) continue;
try
{
var detailResp = await _http.GetAsync($"{_baseUrl}/contacts/{id}");
if (detailResp.IsSuccessStatusCode)
{
var detailBody = await detailResp.Content.ReadAsStringAsync();
var detailObj = JObject.Parse(detailBody);
if (firstPage)
{
OnDebug?.Invoke($"Starface Kontakt-Detail (1. Kontakt):\n{detailObj.ToString(Formatting.Indented)}");
firstPage = false;
}
contacts.Add(MapFromStarface(detailObj));
}
}
catch { }
}
if (array.Count < pageSize) break;
page++;
@@ -255,7 +284,10 @@ namespace StarfaceOutlookSync.Services
var contact = new UnifiedContact();
contact.StarfaceId = item["id"]?.ToString() ?? "";
var attrs = new Dictionary<string, string>();
// Attribute per "name"-Feld mappen (zuverlaessiger als displayKey,
// weil viele Felder USER_DEFINED als displayKey haben)
var byName = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var byDisplayKey = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var blocks = item["blocks"] as JArray;
if (blocks != null)
{
@@ -265,79 +297,123 @@ namespace StarfaceOutlookSync.Services
if (blockAttrs == null) continue;
foreach (var attr in blockAttrs)
{
var key = attr["displayKey"]?.ToString() ?? "";
var name = attr["name"]?.ToString() ?? "";
var displayKey = attr["displayKey"]?.ToString() ?? "";
var val = attr["value"]?.ToString() ?? "";
if (!string.IsNullOrEmpty(val))
attrs[key] = val;
{
if (!string.IsNullOrEmpty(name))
byName[name] = val;
// displayKey nur als Fallback (viele sind USER_DEFINED)
if (!string.IsNullOrEmpty(displayKey) && displayKey != "USER_DEFINED")
byDisplayKey[displayKey] = val;
}
}
}
}
contact.FirstName = attrs.GetValueOrDefault("NAME", "");
contact.LastName = attrs.GetValueOrDefault("SURNAME", "");
contact.Company = attrs.GetValueOrDefault("COMPANY", "");
contact.JobTitle = attrs.GetValueOrDefault("JOB_TITLE", "");
contact.Email = attrs.GetValueOrDefault("EMAIL", "");
contact.PhoneWork = attrs.GetValueOrDefault("OFFICE_PHONE_NUMBER", "");
contact.PhoneMobile = attrs.GetValueOrDefault("MOBILE_PHONE_NUMBER", "");
contact.PhoneHome = attrs.GetValueOrDefault("PRIVATE_PHONE_NUMBER", "");
contact.Fax = attrs.GetValueOrDefault("FAX_NUMBER", "");
contact.Street = attrs.GetValueOrDefault("STREET", "");
contact.City = attrs.GetValueOrDefault("CITY", "");
contact.PostalCode = attrs.GetValueOrDefault("POSTAL_CODE", "");
contact.State = attrs.GetValueOrDefault("STATE", "");
contact.Country = attrs.GetValueOrDefault("COUNTRY", "");
contact.Website = attrs.GetValueOrDefault("URL", "");
contact.Notes = attrs.GetValueOrDefault("NOTE", "");
contact.Salutation = attrs.GetValueOrDefault("SALUTATION", "");
contact.Title = attrs.GetValueOrDefault("TITLE", "");
contact.Birthday = attrs.GetValueOrDefault("BIRTHDAY", "");
// Primaer nach name-Feld mappen, Fallback auf displayKey
string Get(string name, string displayKey = null)
{
if (byName.TryGetValue(name, out var v)) return v;
if (displayKey != null && byDisplayKey.TryGetValue(displayKey, out v)) return v;
return "";
}
contact.FirstName = Get("firstname", "NAME");
contact.LastName = Get("familyname", "SURNAME");
contact.Company = Get("company", "COMPANY");
contact.JobTitle = Get("jobtitle", "JOB_TITLE");
contact.Email = Get("e-mail", "EMAIL");
contact.PhoneWork = Get("phone", "PHONE_NUMBER");
contact.PhoneMobile = Get("mobile", "MOBILE_PHONE_NUMBER");
contact.PhoneHome = Get("homephone", "PRIVATE_PHONE_NUMBER");
contact.Fax = Get("fax", "FAX_NUMBER");
contact.Street = Get("street", "STREET");
contact.City = Get("city", "CITY");
contact.PostalCode = Get("postcode", "POSTAL_CODE");
contact.State = Get("state", "STATE");
contact.Country = Get("country", "COUNTRY");
contact.Website = Get("url", "URL");
contact.Notes = Get("comment", "NOTE");
contact.Salutation = Get("salutation", "SALUTATION");
contact.Title = Get("title", "TITLE");
contact.Birthday = Get("birthday", "BIRTHDAY");
return contact;
}
private JObject MapToStarface(UnifiedContact contact)
{
var attrs = new JArray();
void AddAttr(string displayKey, string name, string value)
JArray MakeAttrs(params (string displayKey, string name, string value)[] fields)
{
if (!string.IsNullOrEmpty(value))
attrs.Add(new JObject { ["displayKey"] = displayKey, ["name"] = name, ["value"] = value });
var arr = new JArray();
foreach (var (dk, n, v) in fields)
{
if (!string.IsNullOrEmpty(v))
arr.Add(new JObject { ["displayKey"] = dk, ["name"] = n, ["value"] = v });
}
return arr;
}
AddAttr("NAME", "firstName", contact.FirstName);
AddAttr("SURNAME", "lastName", contact.LastName);
AddAttr("COMPANY", "company", contact.Company);
AddAttr("JOB_TITLE", "jobTitle", contact.JobTitle);
AddAttr("EMAIL", "email", contact.Email);
AddAttr("OFFICE_PHONE_NUMBER", "businessPhone", contact.PhoneWork);
AddAttr("MOBILE_PHONE_NUMBER", "mobilePhone", contact.PhoneMobile);
AddAttr("PRIVATE_PHONE_NUMBER", "homePhone", contact.PhoneHome);
AddAttr("FAX_NUMBER", "fax", contact.Fax);
AddAttr("STREET", "street", contact.Street);
AddAttr("CITY", "city", contact.City);
AddAttr("POSTAL_CODE", "postalCode", contact.PostalCode);
AddAttr("STATE", "state", contact.State);
AddAttr("COUNTRY", "country", contact.Country);
AddAttr("URL", "website", contact.Website);
AddAttr("NOTE", "notes", contact.Notes);
AddAttr("SALUTATION", "salutation", contact.Salutation);
AddAttr("TITLE", "title", contact.Title);
AddAttr("BIRTHDAY", "birthday", contact.Birthday);
// Block-Struktur wie von der Starface erwartet
var contactBlock = MakeAttrs(
("NAME", "firstname", contact.FirstName),
("SURNAME", "familyname", contact.LastName),
("COMPANY", "company", contact.Company)
);
var addressBlock = MakeAttrs(
("USER_DEFINED", "street", contact.Street),
("POSTAL_CODE", "postcode", contact.PostalCode),
("USER_DEFINED", "city", contact.City),
("USER_DEFINED", "state", contact.State),
("USER_DEFINED", "country", contact.Country)
);
var phoneBlock = MakeAttrs(
("PHONE_NUMBER", "phone", contact.PhoneWork),
("PRIVATE_PHONE_NUMBER", "homephone", contact.PhoneHome),
("MOBILE_PHONE_NUMBER", "mobile", contact.PhoneMobile),
("FAX_NUMBER", "fax", contact.Fax)
);
var emailBlock = MakeAttrs(
("EMAIL", "e-mail", contact.Email),
("URL", "url", contact.Website),
("USER_DEFINED", "comment", contact.Notes)
);
var blocks = new JArray();
blocks.Add(new JObject
{
["name"] = "contact",
["resourceKey"] = "de.vertico.starface.addressbook.block.label_contact",
["attributes"] = contactBlock
});
blocks.Add(new JObject
{
["name"] = "address",
["resourceKey"] = "de.vertico.starface.addressbook.block.label_address",
["attributes"] = addressBlock
});
blocks.Add(new JObject
{
["name"] = "telephone",
["resourceKey"] = "de.vertico.starface.addressbook.block.label_telephone",
["attributes"] = phoneBlock
});
blocks.Add(new JObject
{
["name"] = "email",
["resourceKey"] = "de.vertico.starface.addressbook.block.label_email",
["attributes"] = emailBlock
});
return new JObject
{
["id"] = contact.StarfaceId ?? "",
["blocks"] = new JArray
{
new JObject
{
["name"] = "contact",
["resourceKey"] = "contact",
["attributes"] = attrs
}
}
["blocks"] = blocks
};
}
@@ -53,6 +53,7 @@ namespace StarfaceOutlookSync.Services
Log("Verbinde mit Starface...");
using (var starface = new StarfaceApiClient(profile.StarfaceConnection))
{
starface.OnDebug += (msg) => Log(msg);
var loginOk = await starface.LoginAsync();
if (!loginOk)
{
@@ -7,9 +7,9 @@
<AssemblyTitle>Starface Outlook Sync</AssemblyTitle>
<Company>HackerSoft - Hacker-Net Telekommunikation</Company>
<Product>Starface Outlook Sync</Product>
<Version>0.0.0.8</Version>
<AssemblyVersion>0.0.0.8</AssemblyVersion>
<FileVersion>0.0.0.8</FileVersion>
<Version>0.0.0.12</Version>
<AssemblyVersion>0.0.0.12</AssemblyVersion>
<FileVersion>0.0.0.12</FileVersion>
<Description>Synchronisiert Outlook-Kontakte mit Starface Telefonanlage</Description>
<Copyright>Stefan Hacker - HackerSoft</Copyright>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
+1 -1
View File
@@ -27,7 +27,7 @@ namespace StarfaceOutlookSync.UI
var lblVersion = new Label
{
Text = "Version 0.0.0.8",
Text = "Version 0.0.0.12",
Left = 0, Top = 56, Width = 340, Height = 20,
TextAlign = ContentAlignment.MiddleCenter,
ForeColor = Color.Gray