feat: add msglib message source metadata

This commit is contained in:
zhaoyilun
2026-07-10 08:36:34 +08:00
parent 1a21a01184
commit c8ecf5b36e
8 changed files with 380 additions and 15 deletions

View File

@@ -8,7 +8,7 @@ namespace MsgLibReadSidecar
{
internal static class Program
{
private const string Version = "0.2.0";
private const string Version = "0.3.0";
private const string DefaultPassword = "123";
private static readonly HashSet<string> DefaultTargetTables = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
@@ -46,6 +46,9 @@ namespace MsgLibReadSidecar
case "display_sources":
response = SidecarProtocol.Success(requestId, op, SchemaSummary(opArgs, true));
break;
case "message_sources":
response = SidecarProtocol.Success(requestId, op, MessageSources(opArgs));
break;
case "display_entities":
response = SidecarProtocol.Success(requestId, op, DisplayEntities(opArgs));
break;
@@ -123,6 +126,40 @@ namespace MsgLibReadSidecar
return result;
}
private static Dictionary<string, object> MessageSources(Dictionary<string, object> args)
{
string sqliteDllPath = SidecarProtocol.GetString(args, "sqlite_dll_path", "");
string dbPath = SidecarProtocol.GetString(args, "db_path", "");
string password = SidecarProtocol.GetString(args, "password", DefaultPassword);
ValidateInputs(sqliteDllPath, dbPath);
var result = new Dictionary<string, object>
{
{ "metadata_only", true },
{ "read_only", true },
{ "message_body_values_returned", false },
{ "raw_rows_returned", false },
{ "file_paths_returned", false },
{ "provider", "System.Data.SQLite" },
{ "process_bits", IntPtr.Size * 8 }
};
using (DbConnection connection = OpenSqliteConnection(sqliteDllPath, dbPath, password))
{
connection.Open();
TryExecuteNonQuery(connection, "PRAGMA query_only=ON");
List<Dictionary<string, object>> tables = ReadMasterTables(connection, 256);
HashSet<string> messageTables = BuildMessageSourceTableSet();
List<Dictionary<string, object>> columns = ReadColumns(connection, tables, messageTables);
Dictionary<string, HashSet<string>> columnMap = BuildColumnMap(columns);
Dictionary<string, string> rowCounts = ReadTargetCountMap(connection, tables, messageTables);
result["message_sources"] = BuildMessageSources(tables, columnMap, rowCounts);
}
return result;
}
private static Dictionary<string, object> DisplayEntities(Dictionary<string, object> args)
{
@@ -288,6 +325,16 @@ namespace MsgLibReadSidecar
return rows;
}
private static Dictionary<string, string> ReadTargetCountMap(DbConnection connection, List<Dictionary<string, object>> tables, HashSet<string> targetTables)
{
var rowCounts = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (Dictionary<string, object> item in ReadTargetCounts(connection, tables, targetTables))
{
rowCounts[Convert.ToString(item["table"])] = Convert.ToString(item["row_count"]);
}
return rowCounts;
}
private static List<Dictionary<string, object>> BuildDisplaySources(List<Dictionary<string, object>> tables, List<Dictionary<string, object>> columns)
{
var tableSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
@@ -320,6 +367,115 @@ namespace MsgLibReadSidecar
return sources;
}
private sealed class MessageSourceSpec
{
public string Source;
public string Table;
public string Role;
public string[] RequiredColumns;
}
private static HashSet<string> BuildMessageSourceTableSet()
{
var tables = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (MessageSourceSpec spec in BuildMessageSourceSpecs())
{
tables.Add(spec.Table);
}
return tables;
}
private static List<MessageSourceSpec> BuildMessageSourceSpecs()
{
return new List<MessageSourceSpec>
{
new MessageSourceSpec
{
Source = "direct_messages",
Table = "tblPersonMsg",
Role = "direct",
RequiredColumns = new string[] { "Id", "SndPersonId", "RecvPersonId", "ObjPersonId", "ActionTime", "MsgText", "MessageBody" }
},
new MessageSourceSpec
{
Source = "group_messages",
Table = "tblMsgGroupPersonMsg",
Role = "group",
RequiredColumns = new string[] { "Id", "SndPersonId", "MsgGroupId", "MsgGroupName", "MsgTime", "MsgText", "MessageBody" }
},
new MessageSourceSpec
{
Source = "system_messages",
Table = "TD_SystemMessageRecord",
Role = "system",
RequiredColumns = new string[] { "MsgGuid", "MsgType", "MsgTitle", "MsgText", "SendPersonJid", "MsgTime", "MsgReadState" }
},
new MessageSourceSpec
{
Source = "group_receipts",
Table = "TD_GroupReceiptMessage",
Role = "receipt_metadata",
RequiredColumns = new string[] { "MsgGuid", "PersonID", "ReceiptTime" }
},
new MessageSourceSpec
{
Source = "received_file_metadata",
Table = "TD_ReceiveFileRecord",
Role = "attachment_metadata",
RequiredColumns = new string[] { "ReceiveMsgGuid", "FileName", "FileSize", "SourceID", "SourceName", "FileType", "SendPersonJid", "SendPersonName", "SendTime" }
},
new MessageSourceSpec
{
Source = "recent_conversation_index",
Table = "tblRecent",
Role = "conversation_index",
RequiredColumns = new string[] { "Id", "PersonId", "PersonName", "ActionTime", "FType" }
}
};
}
private static List<Dictionary<string, object>> BuildMessageSources(List<Dictionary<string, object>> tables, Dictionary<string, HashSet<string>> columnMap, Dictionary<string, string> rowCounts)
{
var tableSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (Dictionary<string, object> table in tables)
{
if (string.Equals(Convert.ToString(table["type"]), "table", StringComparison.OrdinalIgnoreCase))
{
tableSet.Add(Convert.ToString(table["name"]));
}
}
var sources = new List<Dictionary<string, object>>();
foreach (MessageSourceSpec spec in BuildMessageSourceSpecs())
{
AddMessageSource(sources, tableSet, columnMap, rowCounts, spec);
}
return sources;
}
private static void AddMessageSource(List<Dictionary<string, object>> sources, HashSet<string> tableSet, Dictionary<string, HashSet<string>> columnMap, Dictionary<string, string> rowCounts, MessageSourceSpec spec)
{
var presentColumns = new List<string>();
if (columnMap.ContainsKey(spec.Table))
{
foreach (string column in spec.RequiredColumns)
{
if (columnMap[spec.Table].Contains(column)) presentColumns.Add(column);
}
}
string rowCount = rowCounts.ContainsKey(spec.Table) ? rowCounts[spec.Table] : "0";
sources.Add(new Dictionary<string, object>
{
{ "source", spec.Source },
{ "table", spec.Table },
{ "role", spec.Role },
{ "available", tableSet.Contains(spec.Table) && presentColumns.Count == spec.RequiredColumns.Length },
{ "required_columns", spec.RequiredColumns },
{ "present_columns", presentColumns.ToArray() },
{ "row_count", rowCount }
});
}
private sealed class DisplayEntitySpec
{