diff --git a/Core/Core.csproj b/Core/Core.csproj index b92abb41..4e96f1c7 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -24,15 +24,13 @@ - + - - diff --git a/Materials/chemistry.png b/Materials/chemistry.png deleted file mode 100644 index b64d3e3d..00000000 Binary files a/Materials/chemistry.png and /dev/null differ diff --git a/Materials/cmd.png b/Materials/cmd.png deleted file mode 100644 index 1909b974..00000000 Binary files a/Materials/cmd.png and /dev/null differ diff --git a/Materials/maths.png b/Materials/maths.png deleted file mode 100644 index 39532ebf..00000000 Binary files a/Materials/maths.png and /dev/null differ diff --git a/Materials/messages.png b/Materials/messages.png deleted file mode 100644 index 44a8a74a..00000000 Binary files a/Materials/messages.png and /dev/null differ diff --git a/Materials/web.png b/Materials/web.png deleted file mode 100644 index 86ea5b01..00000000 Binary files a/Materials/web.png and /dev/null differ diff --git a/Messages/Data/Code128.cs b/Messages/Data/Code128.cs deleted file mode 100644 index ddf18da0..00000000 --- a/Messages/Data/Code128.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Trivial.Data; - -/// -/// The code-128, which is a high-density linear barcode symbology defined in ISO/IEC 15417:2007. -/// It is used for alphanumeric or numeric-only barcodes. -/// -public partial class Code128 -{ - /// - /// The sub-types. - /// - public enum Subtypes : byte - { - /// - /// Code A. - /// - A = 1, - - /// - /// Code B. - /// - B = 2, - - /// - /// Code C. - /// - C = 3 - } - - /// - /// The output string format. - /// - public enum Formats : byte - { - /// - /// Regular format. - /// - Regular = 0, - - /// - /// Text only. - /// - Text = 1, - - /// - /// Symbol values. - /// - Values = 2, - - /// - /// Hex values. - /// - Hex = 3, - - /// - /// The barcode areas that white represented as 0 and black represented as 1. - /// - Barcode = 4, - - /// - /// The stroke path data used in SVG and XAML. - /// - Path = 5 - } - - /// - /// All patterns. - /// - private readonly static List patterns = new() { "11011001100", "11001101100", "11001100110", "10010011000", "10010001100", "10001001100", "10011001000", "10011000100", "10001100100", "11001001000", "11001000100", "11000100100", "10110011100", "10011011100", "10011001110", "10111001100", "10011101100", "10011100110", "11001110010", "11001011100", "11001001110", "11011100100", "11001110100", "11101101110", "11101001100", "11100101100", "11100100110", "11101100100", "11100110100", "11100110010", "11011011000", "11011000110", "11000110110", "10100011000", "10001011000", "10001000110", "10110001000", "10001101000", "10001100010", "11010001000", "11000101000", "11000100010", "10110111000", "10110001110", "10001101110", "10111011000", "10111000110", "10001110110", "11101110110", "11010001110", "11000101110", "11011101000", "11011100010", "11011101110", "11101011000", "11101000110", "11100010110", "11101101000", "11101100010", "11100011010", "11101111010", "11001000010", "11110001010", "10100110000", "10100001100", "10010110000", "10010000110", "10000101100", "10000100110", "10110010000", "10110000100", "10011010000", "10011000010", "10000110100", "10000110010", "11000010010", "11001010000", "11110111010", "11000010100", "10001111010", "10100111100", "10010111100", "10010011110", "10111100100", "10011110100", "10011110010", "11110100100", "11110010100", "11110010010", "11011011110", "11011110110", "11110110110", "10101111000", "10100011110", "10001011110", "10111101000", "10111100010", "11110101000", "11110100010", "10111011110", "10111101110", "11101011110", "11110101110", "11010000100", "11010010000", "11010011100", "11000111010" }; - - /// - /// Gets the pattern of the specific symbol value. - /// White represented as false, black represented as true. - /// - /// The symbol value. - /// The areas in boolean collection. - /// The value was greater than 106. - public static List GetPattern(byte value) - => GetPatternInternal(value).ToList(); - - /// - /// Gets the pattern of the specific symbol value. - /// - /// The symbol value. - /// The barcode string. - /// The value was greater than 106. - public static string GetPatternString(byte value) - => value < 107 ? patterns[value] : throw new ArgumentOutOfRangeException(nameof(value), "value should be less than 107."); - - /// - /// Gets the pattern of the specific symbol value. - /// - /// The symbol value. - /// The value of black represented. - /// The value of white represented. - /// The barcode string. - /// The value was greater than 106. - public static string ToBarcodeString(byte value, char black, char white) - => ToBarcodeString(value, ele => ele ? black : white); - - /// - /// Gets the pattern of the specific symbol value. - /// - /// The symbol value. - /// The selector to convert boolean array to a string. Per boolean value, white represented as false, black represented as true. - /// The barcode string. - /// The value was greater than 106. - public static string ToBarcodeString(byte value, Func selector) - => string.Join(string.Empty, GetPatternInternal(value).Select(selector)); - - /// - /// Gets the pattern of the specific symbol value. - /// - /// The symbol value. - /// The selector to convert boolean array to a string. Per boolean value, white represented as false, black represented as true. - /// The barcode string. - /// The value was greater than 106. - public static string ToBarcodeString(byte value, Func selector) - => string.Join(string.Empty, GetPatternInternal(value).Select(selector)); - - /// - /// Converts a symbol value to string. - /// - /// The sub-type. - /// The symbol value. - /// A string represented. - /// - public static string ToString(Subtypes subtype, byte value) - { - if (value > 101) return value switch - { - 102 => "[FNC1]", - 103 => "[Start A]", - 104 => "[Start B]", - 105 => "[Start C]", - 106 => "[Stop]", - _ => string.Empty - }; - if (subtype == Subtypes.C) - return value < 100 ? value.ToString("g") : value switch - { - 100 => "[Code B]", - 101 => "[Code A]", - _ => string.Empty - }; - - var isA = subtype == Subtypes.A; - if (!isA && subtype != Subtypes.B) - throw new InvalidOperationException("subtype is not valid."); - if (value < 64 || (!isA && value < 96)) - return new string((char)(value + 32), 1); - if (value < 96) - return new string((char)(value - 64), 1); - return value switch - { - 96 => "[FNC3]", - 97 => "[FNC2]", - 98 => isA ? "[Shift B]" : "[Shift A]", - 99 => "[Code C]", - 100 => isA ? "[Code B]" : "[FNC4]", - 101 => isA ? "[FNC4]" : "[Code A]", - _ => string.Empty - }; - } - - /// - /// Gets the pattern of the specific symbol value. - /// - /// The symbol value. - /// - /// The value was greater than 106. - private static IEnumerable GetPatternInternal(byte value) - { - if (value > 106) throw new ArgumentOutOfRangeException(nameof(value), "value should be less than 107."); - var item = patterns[value]; - return item.Select(c => c switch - { - '0' => false, - _ => true - }); - } -} diff --git a/Messages/Data/Code128AI.cs b/Messages/Data/Code128AI.cs deleted file mode 100644 index ea5792c0..00000000 --- a/Messages/Data/Code128AI.cs +++ /dev/null @@ -1,770 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Trivial.Data; - -/// -/// The code-128, which is a high-density linear barcode symbology defined in ISO/IEC 15417:2007. -/// It is used for alphanumeric or numeric-only barcodes. -/// -public partial class Code128 -{ -#pragma warning disable IDE0056 - /// - /// Commonly used GS1-128 generator which identifies data with Application Identifiers. - /// - public static class Gs1Generator - { - /* https://www.gs1.org/standards/barcodes/application-identifiers */ - - /// - /// Creates serial shipping container code. - /// - /// The data without AI, length should be 18. - /// The code 128 instance. - public static Code128 Sscc(string data) - => CreateGs1(0, data); - - /// - /// Creates global trade item number. - /// - /// The data without AI, length should be 14. - /// The code 128 instance. - public static Code128 Gtin(string data) - => CreateGs1(1, data); - - /// - /// Creates global trade item number of contained trade items. - /// - /// The data without AI, length should be 14. - /// The code 128 instance. - public static Code128 GtinOfContainedTradeItems(string data) - => CreateGs1(2, data); - - /// - /// Creates batch number or lot number. - /// - /// The data without AI, length is up to 20. - /// The code 128 instance. - public static Code128 Batch(string data) - => CreateGs1(10, data); - - /// - /// Creates production date. - /// - /// The data without AI. - /// The code 128 instance. - public static Code128 Production(DateTime data) - => CreateGs1Date(11, data); - - /// - /// Creates due date. - /// - /// The data without AI. - /// The code 128 instance. - public static Code128 Due(DateTime data) - => CreateGs1Date(12, data); - - /// - /// Creates packaging date. - /// - /// The data without AI. - /// The code 128 instance. - public static Code128 Packaging(DateTime data) - => CreateGs1Date(13, data); - - /// - /// Creates best before date. - /// - /// The data without AI. - /// The code 128 instance. - public static Code128 BestBefore(DateTime data) - => CreateGs1Date(15, data); - - /// - /// Creates expiration date. - /// - /// The data without AI. - /// The code 128 instance. - public static Code128 Expiration(DateTime data) - => CreateGs1Date(17, data); - - /// - /// Creates internal product variant. - /// - /// The data without AI, should be less than 100. - /// The code 128 instance. - public static Code128 ProductVariant(byte data) - => CreateGs1(20, data.ToString("g")); - - /// - /// Creates serial number. - /// - /// The data without AI, length is up to 20. - /// The code 128 instance. - public static Code128 Sn(string data) - => CreateGs1(21, data); - - /// - /// Creates secondary data fields. - /// - /// The data without AI, length should be 29. - /// The code 128 instance. - public static Code128 SecondaryData(string data) - => CreateGs1(22, data); - - /// - /// Creates additional product identification. - /// - /// The data without AI, length is up to 30. - /// The code 128 instance. - public static Code128 AdditionalProductIdentification(string data) - => CreateGs1(240, data); - - /// - /// Creates customer part number. - /// - /// The data without AI, length is up to 30. - /// The code 128 instance. - public static Code128 CustomerPart(string data) - => CreateGs1(241, data); - - /// - /// Creates Made-to-Order Variation Number. - /// - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - public static Code128 MadeToOrderVariation(int data) - => CreateGs1(242, data.ToString("g")); - - /// - /// Creates packaging component number. - /// - /// The data without AI, length is up to 20. - /// The code 128 instance. - public static Code128 PackagingComponent(string data) - => CreateGs1(243, data); - - /// - /// Creates secondary serial number. - /// - /// The data without AI, length is up to 30. - /// The code 128 instance. - public static Code128 Sn2(string data) - => CreateGs1(250, data); - - /// - /// Creates reference to source entity. - /// - /// The data without AI, length is up to 30. - /// The code 128 instance. - public static Code128 SourceEntity(string data) - => CreateGs1(251, data); - - /// - /// Creates global document type identifier. - /// - /// The data without AI, length is variable from 13 to 17. - /// The code 128 instance. - public static Code128 DocumentTypeId(string data) - => CreateGs1(253, data); - - /// - /// Creates GLN extension component. - /// - /// The data without AI, length is up to 20. - /// The code 128 instance. - public static Code128 GlnExtensionComponent(string data) - => CreateGs1(254, data); - - /// - /// Creates global coupon number. - /// - /// The data without AI, length is variable from 13 to 25. - /// The code 128 instance. - public static Code128 Gcn(string data) - => CreateGs1(255, data); - - /// - /// Creates count of items. - /// - /// The data without AI, should be less than 1_000_000_000. - /// The code 128 instance. - public static Code128 Count(int data) - => CreateGs1(30, data.ToString("g")); - - /// - /// Creates product net weight in kilo gram (kg). - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ProductWeight(byte y, int data) - => CreateGs1Decimal(3100, y, data); - - /// - /// Creates container gross weight in kilo gram (kg). - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ContainerWeight(byte y, int data) - => CreateGs1Decimal(3300, y, data); - - /// - /// Creates product length in meters. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ProductLength(byte y, int data) - => CreateGs1Decimal(3110, y, data); - - /// - /// Creates container length in meters. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ContainerLength(byte y, int data) - => CreateGs1Decimal(3310, y, null, data); - - /// - /// Creates product width/diamete in meters. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ProductWidth(byte y, int data) - => CreateGs1Decimal(3120, y, data); - - /// - /// Creates container width/diamete in meters. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ContainerWidth(byte y, int data) - => CreateGs1Decimal(3320, y, data); - - /// - /// Creates product depth/thickness/height in meters. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ProductDepth(byte y, int data) - => CreateGs1Decimal(3130, y, data); - - /// - /// Creates container depth/thickness/height in meters. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ContainerDepth(byte y, int data) - => CreateGs1Decimal(3330, y, data); - - /// - /// Creates product area in square meters. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ProductArea(byte y, int data) - => CreateGs1Decimal(3140, y, data); - - /// - /// Creates container area in square meters. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ContainerArea(byte y, int data) - => CreateGs1Decimal(3340, y, data); - - /// - /// Creates product net volume in liters. - /// - /// A number of decimal places in the following value. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ProductVolume(byte y, int data) - => y > 9 ? CreateGs1Decimal(3160, (byte)(y - 10), data) : CreateGs1Decimal(3150, y, data); - - /// - /// Creates container gross volume in liters. - /// - /// A number of decimal places in the following value. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 ContainerVolume(byte y, int data) - => y > 9 ? CreateGs1Decimal(3360, (byte)(y - 10), data) : CreateGs1Decimal(3350, y, data); - - /// - /// Creates count of units contained. - /// - /// The data without AI, should be less than 1,000,000,000. - /// The code 128 instance. - public static Code128 UnitCount(int data) - => CreateGs1(37, data.ToString("g")); - - /// - /// Creates applicable amount payable. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000,000,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 AmountPayable(byte y, long data) - => CreateGs1Decimal(3900, y, null, data); - - /// - /// Creates applicable amount payable with optional ISO currency code. - /// - /// A number of decimal places in the following value, should be less than 10. - /// The ISO currency code. - /// The data without AI, should be less than 1,000,000,000,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 AmountPayable(byte y, string currencyCode, long data) - => CreateGs1Decimal(string.IsNullOrEmpty(currencyCode) ? 3900 : 3910, y, currencyCode, data); - - /// - /// Creates applicable amount payable (variable measure trade item). - /// - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000,000,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 TradeItemAmountPayable(byte y, long data) - => CreateGs1Decimal(3920, y, null, data); - - /// - /// Creates applicable amount payable with optional ISO currency code (variable measure trade item). - /// - /// A number of decimal places in the following value, should be less than 10. - /// The ISO currency code. - /// The data without AI, should be less than 1,000,000,000,000,000. - /// The code 128 instance. - /// y or data was out of range. - public static Code128 TradeItemAmountPayable(byte y, string currencyCode, long data) - => CreateGs1Decimal(string.IsNullOrEmpty(currencyCode) ? 3920 : 3930, y, currencyCode, data); - - /// - /// Creates customer purchase order number. - /// - /// The data without AI, length is up to 30. - /// The code 128 instance. - public static Code128 Order(string data) - => CreateGs1(400, data); - - /// - /// Creates consignment number. - /// - /// The data without AI, length is up to 30. - /// The code 128 instance. - public static Code128 Consignment(string data) - => CreateGs1(401, data); - - /// - /// Creates bill of lading number. - /// - /// The data without AI, length should be 17. - /// The code 128 instance. - public static Code128 LadingBill(string data) - => CreateGs1(402, data); - - /// - /// Creates routing code. - /// - /// The data without AI, length is up to 30. - /// The code 128 instance. - public static Code128 Routing(string data) - => CreateGs1(403, data); - - /// - /// Creates ship/deliver to postal code (single postal authority). - /// - /// The data without AI, length is up to 20. - /// The code 128 instance. - public static Code128 ToSinglePostalAuthorityCode(string data) - => CreateGs1(420, data); - - /// - /// Creates ship/deliver to postal code (with ISO country code). - /// - /// The data without AI, length is variable from 3 to 15. - /// The code 128 instance. - public static Code128 ToPostalCode(string data) - => CreateGs1(421, data); - - /// - /// Creates ISO country code of origin. - /// - /// The data without AI, length should be 3. - /// The code 128 instance. - public static Code128 OriginCountry(string data) - => CreateGs1(422, data); - - /// - /// Creates one or more ISO country code of initial processing. - /// - /// The data without AI, length is variable from 3 to 15. - /// The code 128 instance. - public static Code128 InitialProcessingCountries(string data) - => CreateGs1(423, data); - - /// - /// Creates ISO country code of processing. - /// - /// The data without AI, length should be 3. - /// The code 128 instance. - public static Code128 ProcessingCountry(string data) - => CreateGs1(424, data); - - /// - /// Creates ISO country code of disassembly. - /// - /// The data without AI, length should be 3. - /// The code 128 instance. - public static Code128 DisassemblyCountry(string data) - => CreateGs1(425, data); - - /// - /// Creates ISO country code of full process chain. - /// - /// The data without AI, length should be 3. - /// The code 128 instance. - public static Code128 FullProcessChainCountry(string data) - => CreateGs1(426, data); - - /// - /// Creates service code description. - /// - /// The data without AI, length is up to 35. - /// The code 128 instance. - public static Code128 ServiceCodeDescription(string data) - => CreateGs1(3420, data); - - /// - /// Creates dangerous goods flag. - /// - /// true if the data without AI is yes (as 1); otherwise, false. - /// The code 128 instance. - public static Code128 Dangerous(bool data) - => CreateGs1(3421, data ? "1" : "0"); - - /// - /// Creates authority to leave. - /// - /// true if the data without AI is yes (as 1); otherwise, false. - /// The code 128 instance. - public static Code128 AuthorityToLeave(bool data) - => CreateGs1(3422, data ? "1" : "0"); - - /// - /// Creates signature required flag. - /// - /// true if the data without AI is yes (as 1); otherwise, false. - /// The code 128 instance. - public static Code128 SignatureRequired(bool data) - => CreateGs1(3423, data ? "1" : "0"); - - /// - /// Creates release date. - /// - /// The data without AI, length should be 13. - /// The code 128 instance. - public static Code128 Release(DateTime data) - => CreateGs1Date(3426, data); - - /// - /// Creates NATO stock number. - /// - /// The data without AI, length should be 13. - /// The code 128 instance. - public static Code128 Nsn(string data) - => CreateGs1(7001, data); - - /// - /// Creates active potency. - /// - /// The data without AI, length is up to 4. - /// The code 128 instance. - public static Code128 ActivePotency(int data) - => CreateGs1(7004, data.ToString("g")); - - /// - /// Creates catch area. - /// - /// The data without AI, length is up to 12. - /// The code 128 instance. - public static Code128 CatchArea(string data) - => CreateGs1(7005, data); - - /// - /// Creates first freeze date. - /// - /// The data without AI, length should be 13. - /// The code 128 instance. - public static Code128 FirstFreeze(DateTime data) - => CreateGs1Date(7006, data); - - /// - /// Creates harvest date. - /// - /// The data without AI, length should be 13. - /// The code 128 instance. - public static Code128 Harvest(DateTime data) - => CreateGs1Date(7007, data); - - /// - /// Creates production method. - /// - /// The data without AI, length is up to 2. - /// The code 128 instance. - public static Code128 ProductionMethod(string data) - => CreateGs1(7010, data); - - /// - /// Creates certification reference. - /// - /// The certification number, should be 0-9. - /// The data without AI, length is up to 30. - /// The code 128 instance. - /// y ws greater than 9. - public static Code128 Certification(byte y, string data) - => y < 10 ? CreateGs1(7230 + y, data) : throw new ArgumentOutOfRangeException(nameof(y), "y should be less than 10."); - - /// - /// Creates protocol ID. - /// - /// The data without AI, length is up to 20. - /// The code 128 instance. - public static Code128 Protocol(string data) - => CreateGs1(7240, data); - - /// - /// Creates roll products: width/length/core diameter/direction/splices. - /// - /// The data without AI, length should be 14. - /// The code 128 instance. - public static Code128 RollProducts(string data) - => CreateGs1(8001, data); - - /// - /// Creates mobile phone identifier. - /// - /// The data without AI, length is up to 20. - /// The code 128 instance. - public static Code128 PhoneId(string data) - => CreateGs1(8002, data); - - /// - /// Creates global returnable asset identifier. - /// - /// The data without AI, length is variable from 14 to 30. - /// The code 128 instance. - public static Code128 GlobalReturnableAsset(string data) - => CreateGs1(8003, data); - - /// - /// Creates global individual asset identifier. - /// - /// The data without AI, length should be 30. - /// The code 128 instance. - public static Code128 GlobalIndividualAsset(string data) - => CreateGs1(8004, data); - - /// - /// Creates international bank account number. - /// - /// The data without AI, length should be 30. - /// The code 128 instance. - public static Code128 BankAccount(string data) - => CreateGs1(8007, data); - - /// - /// Creates software version. - /// - /// The data without AI, length is up to 20. - /// The code 128 instance. - public static Code128 SoftwareVersion(string data) - => CreateGs1(8012, data); - - /// - /// Creates global model number. - /// - /// The data without AI, length is up to 25. - /// The code 128 instance. - public static Code128 Gmn(string data) - => CreateGs1(8013, data); - - /// - /// Creates global service relationship number. - /// - /// true if identify the relationship between an organisation offering services and the recipient of services; otherwise, false, provider. - /// The data without AI, length should be 18. - /// The code 128 instance. - public static Code128 GlobalServiceRelationship(bool recipient, string data) - => CreateGs1(recipient ? 8018 : 8017, data); - - /// - /// Creates service relation instance number. - /// - /// The data without AI, length is up to 10. - /// The code 128 instance. - public static Code128 Srin(string data) - => CreateGs1(8019, data); - - /// - /// Creates payment slip reference number. - /// - /// The data without AI, length is variable from 13 to 25. - /// The code 128 instance. - public static Code128 PaymentSlip(string data) - => CreateGs1(8020, data); - - /// - /// Creates extended packaging URL. - /// - /// The data without AI, length tp to 70. - /// The code 128 instance. - public static Code128 ExtendedPackagingURL(string data) - => CreateGs1(8200, data); - - /// - /// Creates mutually agreed between trading partners. - /// - /// The data without AI, length is up to 30. - /// The code 128 instance. - public static Code128 MutuallyAgreedBetweenTradingPartners(string data) - => CreateGs1(90, data); - - /// - /// Creates internal company codes (AI 91). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes1(string data) - => CreateGs1(91, data); - - /// - /// Creates internal company codes (AI 92). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes2(string data) - => CreateGs1(92, data); - - /// - /// Creates internal company codes (AI 93). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes3(string data) - => CreateGs1(93, data); - - /// - /// Creates internal company codes (AI 94). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes4(string data) - => CreateGs1(94, data); - - /// - /// Creates internal company codes (AI 95). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes5(string data) - => CreateGs1(95, data); - - /// - /// Creates internal company codes (AI 96). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes6(string data) - => CreateGs1(96, data); - - /// - /// Creates internal company codes (AI 97). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes7(string data) - => CreateGs1(97, data); - - /// - /// Creates internal company codes (AI 98). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes8(string data) - => CreateGs1(98, data); - - /// - /// Creates internal company codes (AI 99). - /// - /// The data without AI, length is up to 90. - /// The code 128 instance. - public static Code128 InternalCompanyCodes9(string data) - => CreateGs1(99, data); - - /// - /// Creates for decimal. - /// - /// The application identifier. - /// A number of decimal places in the following value, should be less than 10. - /// The data without AI, should be less than 1,000,000. - /// The code 128 instance. - /// y or data was out of range. - private static Code128 CreateGs1Decimal(int ai, byte y, int data) - { - if (y > 9) throw new ArgumentOutOfRangeException(nameof(y), "y should be less than 10."); - if (data < 0) throw new ArgumentOutOfRangeException(nameof(data), "data should be in 0-999,999 but it is negative currently."); - if (data > 999_999) throw new ArgumentOutOfRangeException(nameof(data), "data should be in 0-999,999 but it is greater than 999,999 currently."); - return CreateGs1(ai + y, data.ToString("000000")); - } - - /// - /// Creates for decimal. - /// - /// The application identifier. - /// A number of decimal places in the following value, should be less than 10. - /// The prefix of data. - /// The data without AI, should be less than 1,000,000,000. - /// The code 128 instance. - /// y or data was out of range. - private static Code128 CreateGs1Decimal(int ai, byte y, string prefix, long data) - { - if (y > 9) throw new ArgumentOutOfRangeException(nameof(y), "y should be less than 10."); - if (data < 0) throw new ArgumentOutOfRangeException(nameof(data), "data should be in 0-999,999,999,999,999 but it is negative currently."); - if (data > 999_999_999_999_999) throw new ArgumentOutOfRangeException(nameof(data), "data should be in 0-999,999,999,999,999, but it is greater than 999,999,999,999,999 currently."); - return CreateGs1(ai + y, string.IsNullOrEmpty(prefix) ? data.ToString("g") : $"{prefix}{data:g}"); - } - } -#pragma warning restore IDE0056 - - private static Code128 CreateGs1Date(int ai, DateTime date) - => CreateGs1(ai, date.ToString("yyMMdd")); -} diff --git a/Messages/Data/Code128Builder.cs b/Messages/Data/Code128Builder.cs deleted file mode 100644 index 32595c36..00000000 --- a/Messages/Data/Code128Builder.cs +++ /dev/null @@ -1,1063 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Trivial.Data; - -/// -/// The code-128, which is a high-density linear barcode symbology defined in ISO/IEC 15417:2007. -/// It is used for alphanumeric or numeric-only barcodes. -/// -public partial class Code128 : IReadOnlyList -{ -#pragma warning disable IDE0056 - private readonly List v; - - /// - /// Initializes a new instance of Code128 class. - /// - /// The byte array of symbol. - private Code128(List values) - { - v = values ?? new List(); - } - - /// - /// Gets the count of symbol. - /// - public int Count => v.Count; - - /// - /// Gets the start sub-type. - /// - public Subtypes Subtype => v.Count > 3 ? v[0] switch - { - 103 => Subtypes.A, - 104 => Subtypes.B, - 105 => Subtypes.C, - _ => (Subtypes)0 - } : 0; - - /// - /// Gets the checksum; or 255, if not available. - /// - public byte Checksum => v.Count > 3 ? v[v.Count - 2] : (byte)255; - - /// - /// Gets the value of the specific symbol. - /// - /// The index of symbol. - /// The value of the symbol. - /// The index was out of range. - public byte this[int index] => v[index]; - - /// - /// Returns symbol values without start code, checksum, and stop code. - /// - /// A collection of symbol value without start code, checksum, and stop code. - public IEnumerable TakeData() - => v.Count > 3 ? v.Skip(1).Take(v.Count - 3) : new List(); - - /// - /// Converts to boolean list. - /// White represented as false, black represented as true. - /// - /// The boolean list of barcode. - public List ToBarcode() - { - var list = v.SelectMany(GetPattern).ToList(); - if (list.Count > 10) - { - list.Add(true); - list.Add(true); - } - - return list; - } - - /// - /// Gets all information of application identifier and its data value. - /// - /// A collection with application identifiers and their data value. - public IEnumerable GetAiData() - { - if (v.Count < 4) yield break; - var subtype = v[0] switch - { - 103 => Subtypes.A, - 104 => Subtypes.B, - 105 => Subtypes.C, - _ => (Subtypes)0 - }; - if (subtype == 0) yield break; - var sb = new StringBuilder(); - var subtype2 = subtype; - var high = false; - var rec = false; - foreach (var b in v.Skip(1).Take(v.Count - 3)) - { - if (b > 101) - { - if (b > 102) break; - rec = true; - if (sb.Length > 0) - { - yield return sb.ToString(); - sb.Clear(); - } - - continue; - } - - var st = subtype; - subtype = subtype2; - if (st == Subtypes.C) - { - high = false; - if (b < 100 || b == 102) - { - if (rec) sb.Append(b.ToString("00")); - continue; - } - - switch (b) - { - case 100: - subtype = subtype2 = Subtypes.B; - break; - case 101: - subtype = subtype2 = Subtypes.A; - break; - } - - continue; - } - - var isA = st == Subtypes.A; - if (!isA && st != Subtypes.B) - { - high = false; - continue; - } - - if (b < 96) - { - if (high) - { - high = false; - var c = (char)(ToString(st, b).FirstOrDefault() + 128); - if (rec) sb.Append(c); - } - else - { - if (rec) sb.Append(ToString(st, b)); - } - - continue; - } - - switch (b) - { - case 96: - rec = false; - if (sb.Length > 0) - { - yield return sb.ToString(); - sb.Clear(); - } - - break; - case 97: - rec = false; - if (sb.Length > 0) - { - yield return sb.ToString(); - sb.Clear(); - } - - break; - case 98: - subtype = isA ? Subtypes.B : Subtypes.A; - continue; - case 99: - subtype = subtype2 = Subtypes.C; - break; - case 100: - if (!isA) - { - high = !high; - continue; - } - - subtype = subtype2 = Subtypes.B; - break; - case 101: - if (isA) - { - high = !high; - continue; - } - - subtype = subtype2 = Subtypes.A; - break; - } - - high = false; - } - - if (sb.Length > 0) yield return sb.ToString(); - } - - /// - /// Returns a string that represents the barcode in stroke path for SVG or XAML. - /// - /// The bar height. - /// A stroke path string that represents the barcode. - public string ToPathString(int height = 40) - { - var sb = new StringBuilder(); - var i = 9; - sb.Append("M0,0 "); - foreach (var b in v) - { - if (b > 106) continue; - var binary = patterns[b]; - foreach (var c in binary) - { - i++; - if (c == '1') - sb.Append($"M{i},0 L{i},{height} "); - } - } - - sb.Append($"M{i + 1},{0} L{i + 1},{height} M{i + 2},{0} L{i + 2},{height} M{i + 12},0"); - return sb.ToString(); - } - - /// - /// Returns a string that represents the current object. - /// - /// A string that represents the current object. - public override string ToString() - => ToString(Formats.Regular); - - /// - /// Returns a string that represents the current object. - /// - /// The output string format. - /// A string that represents the current object. - public string ToString(Formats format) - { - if (v.Count < 4) return string.Empty; - var subtype = v[0] switch - { - 103 => Subtypes.A, - 104 => Subtypes.B, - 105 => Subtypes.C, - _ => (Subtypes)0 - }; - if (subtype == 0) return string.Empty; - var sb = new StringBuilder(); - switch (format) - { - case Formats.Regular: - case Formats.Text: - break; - case Formats.Hex: - foreach (var b in v) - { - sb.Append(b.ToString("x2")); - } - - return sb.ToString(); - case Formats.Values: - foreach (var b in v.Take(v.Count - 2)) - { - if (subtype == Subtypes.C && b < 100) - { - sb.Append(b); - sb.Append(' '); - continue; - } - - switch (b) - { - case 96: - sb.Append("[FNC3] "); - break; - case 97: - sb.Append("[FNC2] "); - break; - case 98: - sb.Append(subtype == Subtypes.A ? "[Shift B] " : "[Shift A] "); - break; - case 99: - subtype = Subtypes.C; - sb.Append("[Code C] "); - break; - case 100: - if (subtype == Subtypes.B) - { - sb.Append("[FNC4] "); - } - else - { - subtype = Subtypes.B; - sb.Append("[Code B] "); - } - - break; - case 101: - if (subtype == Subtypes.A) - { - sb.Append("[FNC4] "); - } - else - { - subtype = Subtypes.A; - sb.Append("[Code A] "); - } - - break; - case 102: - sb.Append("[FNC1] "); - break; - case 103: - sb.Append("[Start A] "); - break; - case 104: - sb.Append("[Start B] "); - break; - case 105: - sb.Append("[Start C] "); - break; - default: - sb.Append(b); - sb.Append(' '); - break; - } - } - - sb.Append($"[Check symbol {v[v.Count - 2]:g}] [Stop]"); - return sb.ToString().Trim(); - case Formats.Barcode: - return ToBarcodeString(); - case Formats.Path: - return ToPathString(40); - } - - var appendFunc = format == Formats.Regular; - var subtype2 = subtype; - var high = false; - var high2 = false; - foreach (var b in v.Skip(1).Take(v.Count - 3)) - { - if (b > 101) - { - if (b > 102) break; - if (appendFunc) sb.Append("[FNC1]"); - high = high2 = false; - continue; - } - - var st = subtype; - subtype = subtype2; - if (st == Subtypes.C) - { - high = high2 = false; - if (b < 100 || b == 102) - { - sb.Append(b.ToString("00")); - continue; - } - - switch (b) - { - case 100: - subtype = subtype2 = Subtypes.B; - break; - case 101: - subtype = subtype2 = Subtypes.A; - break; - } - - continue; - } - - var isA = st == Subtypes.A; - if (!isA && st != Subtypes.B) - { - high = high2 = false; - continue; - } - - if (b < 96) - { - if (high) - { - high = high2; - var c = (char)(ToString(st, b).FirstOrDefault() + 128); - sb.Append(c); - } - else - { - high = high2; - sb.Append(ToString(st, b)); - } - - continue; - } - - switch (b) - { - case 96: - if (appendFunc) sb.Append("[FNC3]"); - break; - case 97: - if (appendFunc) sb.Append("[FNC2]"); - break; - case 98: - subtype = isA ? Subtypes.B : Subtypes.A; - continue; - case 99: - subtype = subtype2 = Subtypes.C; - break; - case 100: - if (!isA) - { - if (high != high2) high2 = high; - else high = !high; - continue; - } - - subtype = subtype2 = Subtypes.B; - break; - case 101: - if (isA) - { - if (high != high2) high2 = high; - else high = !high; - continue; - } - - subtype = subtype2 = Subtypes.A; - break; - } - - high = high2; - } - - return sb.ToString(); - } - - /// - /// Converts to a string. - /// - /// The barcode string. - public string ToBarcodeString() - { - var sb = new StringBuilder(); - foreach (var b in v) - { - sb.Append(GetPattern(b)); - } - - if (sb.Length > 10) sb.Append("11"); - return sb.ToString(); - } - - /// - /// Converts to a string. - /// - /// The value of black represented. - /// The value of white represented. - /// The barcode string. - public string ToBarcodeString(char black, char white) - => string.Join(string.Empty, ToBarcode().Select(ele => ele ? black : white)); - - /// - /// Converts to a string. - /// - /// The selector to convert boolean array to a string. Per boolean value, white represented as false, black represented as true. - /// The barcode string. - public string ToBarcodeString(Func selector) - => selector != null ? string.Join(string.Empty, ToBarcode().Select(selector)) : ToBarcodeString(); - - /// - /// Converts to a string. - /// - /// The selector to convert boolean array to a string. Per boolean value, white represented as false, black represented as true. - /// The barcode string. - public string ToBarcodeString(Func selector) - => selector != null ? string.Join(string.Empty, ToBarcode().Select(selector)) : ToBarcodeString(); - - /// - /// Gets all sub-types used in value. - /// - /// A collection of sub-type. - public IEnumerable GetSubtypesUsed() - { - var subtype = Subtype; - yield return subtype; - foreach (var b in v.Skip(1).Take(v.Count - 3)) - { - if (b < 98) continue; - if (b > 102) break; - switch (b) - { - case 99: - if (subtype == Subtypes.C) break; - subtype = Subtypes.C; - yield return subtype; - break; - case 100: - if (subtype == Subtypes.B) break; - subtype = Subtypes.B; - yield return subtype; - break; - case 101: - if (subtype == Subtypes.A) break; - subtype = Subtypes.A; - yield return subtype; - break; - } - } - } - - /// - /// Returns an enumerator that iterates through the symbol collection. - /// - /// A enumerator of the symbol collection. - public IEnumerator GetEnumerator() - => v.GetEnumerator(); - - /// - /// Returns an enumerator that iterates through the symbol collection. - /// - /// A enumerator of the symbol collection. - IEnumerator IEnumerable.GetEnumerator() - => ((IEnumerable)v).GetEnumerator(); - - /// - /// Creates with Start Code A. - /// - /// The value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateA(IEnumerable values) - => Create(values, 103); - - /// - /// Creates with Start Code A. - /// - /// The string value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateA(string s) - => Create(Subtypes.A, s); - - /// - /// Creates with Start Code B. - /// - /// The value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateB(IEnumerable values) - => Create(values, 104); - - /// - /// Creates with Start Code B. - /// - /// The string value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateB(string s) - => Create(Subtypes.B, s); - - /// - /// Creates with Start Code C. - /// - /// The value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateC(IEnumerable values) - => Create(values, 105); - - /// - /// Creates with Start Code C. - /// - /// The string value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateC(string s) - => Create(Subtypes.C, s); - - /// - /// Creates with Start Code C. - /// - /// The integer value of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateC(long s) - => Create(Subtypes.C, s.ToString("g")); - - /// - /// Creates GS1-128 code. - /// - /// The application identifier. - /// The data value. - /// The Code 128 instance. - /// values was null. - /// ai was less than 0. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateGs1(int ai, string data) - { - if (ai < 0) throw new ArgumentOutOfRangeException(nameof(ai), "ai should not be less than zero."); - var col = new List { 102 }; - Fill(col, Subtypes.C, $"{ai:00}{data}"); - return Create(Subtypes.C, col); - } - - /// - /// Creates GS1-128 code. - /// - /// The string 1 with application identifier and its data value. - /// The string 2 with application identifier and its data value. - /// The string 3 with application identifier and its data value. - /// The string 4 with application identifier and its data value. - /// The string 5 with application identifier and its data value. - /// The string 6 with application identifier and its data value. - /// The string 7 with application identifier and its data value. - /// The string 8 with application identifier and its data value. - /// The Code 128 instance. - /// values was null. - /// ai was less than 0. - /// values was empty. - /// Any value is invalid. - public static Code128 CreateGs1(string part1, string part2, string part3 = null, string part4 = null, string part5 = null, string part6 = null, string part7 = null, string part8 = null) - { - var col = new List(); - var subtype = Subtypes.C; - if (!string.IsNullOrEmpty(part1)) - { - col.Add(102); - subtype = Fill(col, Subtypes.C, part1); - } - - if (!string.IsNullOrEmpty(part2)) - { - if (subtype != Subtypes.C) col.Add(99); - col.Add(102); - subtype = Fill(col, Subtypes.C, part2); - } - - if (!string.IsNullOrEmpty(part3)) - { - if (subtype != Subtypes.C) col.Add(99); - col.Add(102); - subtype = Fill(col, Subtypes.C, part3); - } - - if (!string.IsNullOrEmpty(part4)) - { - if (subtype != Subtypes.C) col.Add(99); - col.Add(102); - subtype = Fill(col, Subtypes.C, part4); - } - - if (!string.IsNullOrEmpty(part5)) - { - if (subtype != Subtypes.C) col.Add(99); - col.Add(102); - subtype = Fill(col, Subtypes.C, part5); - } - - if (!string.IsNullOrEmpty(part6)) - { - if (subtype != Subtypes.C) col.Add(99); - col.Add(102); - subtype = Fill(col, Subtypes.C, part6); - } - - if (!string.IsNullOrEmpty(part7)) - { - if (subtype != Subtypes.C) col.Add(99); - col.Add(102); - subtype = Fill(col, Subtypes.C, part7); - } - - if (!string.IsNullOrEmpty(part8)) - { - if (subtype != Subtypes.C) col.Add(99); - col.Add(102); - Fill(col, Subtypes.C, part8); - } - - return Create(Subtypes.C, col); - } - - /// - /// Creates with the specific sub-type. - /// - /// The sub-type to use. - /// The value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 Create(Subtypes subtype, IEnumerable values) - => Create(values, subtype switch - { - Subtypes.A => 103, - Subtypes.B => 104, - Subtypes.C => 105, - _ => throw new InvalidOperationException("subtype is not valid.") - }); - - /// - /// Creates by boolean collection that white represented as false and black represented as true.. - /// - /// The value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 Create(IEnumerable values) - { - if (values == null) throw new ArgumentNullException(nameof(values), "values should not be null."); - var s = string.Join(string.Empty, values.Select(b => b ? '1' : '0')).Trim('0'); - if (s.Length < 25) throw new ArgumentException("The count of values is too less.", nameof(values)); - if (s.StartsWith("1100011101011")) s = new string(s.Reverse().ToArray()); - var col = new List(); - var first = s.Substring(0, 7) switch - { - "11010000100" => 103, - "11010010000" => 104, - "11010011100" => 105, - _ => throw new InvalidOperationException("The start code is not valid.") - }; - for (var i = 1; i < s.Length; i += 7) - { - try - { - var item = s.Substring(i, 7); - var j = patterns.IndexOf(item); - if (j < 0) throw new InvalidOperationException($"Contains invalid symbol at position {i}."); - if (j > 102) - { - if (j == 106) break; - throw new InvalidOperationException($"Contains invalid symbol at position {i}."); - } - } - catch (ArgumentException) - { - } - } - - return Create(col, (byte)first); - } - - /// - /// Creates by symbols. - /// - /// The value collection of symbol. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 Create(IEnumerable values) - { - if (values == null) throw new ArgumentNullException(nameof(values), "values should not be null."); - var first = values.First(); - if (first < 103 || first > 105) throw new InvalidOperationException("The start code is not valid."); - return Create(values.Skip(1), first); - } - - /// - /// Creates with the specific sub-type. - /// - /// The sub-type of start code. - /// The string to encode. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - public static Code128 Create(Subtypes subtype, string s) - { - var col = new List(); - Fill(col, subtype, s); - return Create(subtype, col); - } - - /// - /// Fills symbols. - /// - /// The byte array of symbol value to fill. - /// The sub-type of start code. - /// The string to encode. - /// The subtype of last one. - /// values was null. - /// values was empty. - /// Any value is invalid. - private static Subtypes Fill(List col, Subtypes subtype, string s) - { - if (s == null) return subtype; - var bytes = Encoding.ASCII.GetBytes(s); - byte? reserved = null; - foreach (var b in bytes) - { - if (subtype == Subtypes.A) - { - if (reserved.HasValue) - { - col.Add((byte)(reserved.Value + 16)); - reserved = null; - } - - if (b > 31 && b < 96) - { - col.Add((byte)(b - 32)); - } - else if (b < 32) - { - col.Add((byte)(b + 64)); - } - else if (b > 127) - { - col.Add(101); - var b2 = b - 128; - if (b2 > 31 && b2 < 96) - { - col.Add((byte)(b2 - 32)); - } - else if (b2 < 32) - { - col.Add((byte)(b2 + 64)); - } - else - { - subtype = Subtypes.B; - col.Add(100); - col.Add((byte)(b2 - 32)); - } - } - else - { - subtype = Subtypes.B; - col.Add(100); - col.Add((byte)(b - 32)); - } - } - else if (subtype == Subtypes.B) - { - if (reserved.HasValue) - { - col.Add((byte)(reserved.Value + 16)); - reserved = null; - } - - if (b > 31 && b < 128) - { - col.Add((byte)(b - 32)); - } - else if (b > 127) - { - col.Add(100); - var b2 = b - 128; - if (b2 > 31) - { - col.Add((byte)(b - 32)); - } - else - { - subtype = Subtypes.A; - col.Add(101); - col.Add((byte)(b2 + 64)); - } - } - else - { - subtype = Subtypes.A; - col.Add(101); - col.Add((byte)(b + 64)); - } - } - else if (subtype == Subtypes.C) - { - if (b < 48) - { - if (b < 32) - { - subtype = Subtypes.A; - col.Add(101); - col.Add((byte)(b + 64)); - } - else - { - subtype = Subtypes.B; - col.Add(100); - col.Add((byte)(b - 32)); - } - } - else if (b > 57) - { - if (b > 159) - { - subtype = Subtypes.A; - col.Add(100); - col.Add(100); - col.Add((byte)(b - 160)); - } - else if (b > 127) - { - subtype = Subtypes.A; - col.Add(101); - col.Add(101); - col.Add((byte)(b - 64)); - } - else - { - subtype = Subtypes.B; - col.Add(100); - col.Add((byte)(b - 32)); - } - } - else if (reserved.HasValue) - { - col.Add((byte)(reserved.Value * 10 + b - 48)); - reserved = null; - } - else - { - reserved = (byte)(b - 48); - } - } - else - { - throw new InvalidOperationException($"The subtype is not valid."); - } - } - - if (reserved.HasValue) - { - if (subtype == Subtypes.C) - col.Add(101); - col.Add((byte)(reserved.Value + 16)); - } - - return subtype; - } - - /// - /// Creates with the specific start code. - /// - /// The value collection of symbol. - /// The value of start code. - /// The Code 128 instance. - /// values was null. - /// values was empty. - /// Any value is invalid. - private static Code128 Create(IEnumerable values, byte startCode) - { - if (values == null) throw new ArgumentNullException(nameof(values), "values should not be null."); - var col = values.ToList(); - if (col.Count < 1) throw new ArgumentException("values should not be empty.", nameof(values)); - if (col[0] != startCode) - { - if (col[0] > 102) throw new InvalidOperationException($"The first value {col[0]} is not valid."); - col.Insert(0, startCode); - } - - if (col[col.Count - 1] != 106) - { - long check = startCode; - for (var i = 1; i < col.Count; i++) - { - check += col[i] * i; - } - - col.Add((byte)(check % 103)); - col.Add(106); - } - - var count = col.Count - 1; - for (var i = 1; i < count; i++) - { - if (col[i] > 102) throw new InvalidOperationException($"The value {col[i]} at position {i} is not valid."); - } - - return new Code128(col); - } - - /// - /// Adds all code 128 instances into one. - /// - /// The collection to combine. - /// A code 128 instance combined. - public static Code128 Join(IEnumerable col) - { - if (col is null) return null; - var list = col.Where(ele => ele != null && ele.Count > 3).ToList(); - if (list.Count < 2) return list.Count == 1 ? list[0] : null; - var first = list[0]; - var subtype = first.GetSubtypesUsed().Last(); - var bytes = first.TakeData().ToList(); - foreach (var c in list.Skip(1)) - { - var rightSubtype = c.Subtype; - if (subtype != rightSubtype) bytes.Add(rightSubtype switch - { - Subtypes.A => 101, - Subtypes.B => 100, - _ => 99 - }); - bytes.AddRange(c.TakeData()); - subtype = c.GetSubtypesUsed().Last(); - } - - return Create(first.Subtype, bytes); - } - - /// - /// Adds. - /// leftValue + rightValue - /// - /// The left value. - /// The right value. - /// A result after Add operation. - public static Code128 operator +(Code128 leftValue, Code128 rightValue) - { - if (rightValue is null) return leftValue; - if (leftValue is null) return rightValue; - var subtype = leftValue.GetSubtypesUsed().Last(); - var bytes = leftValue.TakeData().ToList(); - var rightSubtype = rightValue.Subtype; - if (subtype != rightSubtype) bytes.Add(rightSubtype switch - { - Subtypes.A => 101, - Subtypes.B => 100, - _ => 99 - }); - bytes.AddRange(rightValue.TakeData()); - return Create(leftValue.Subtype, bytes); - } -#pragma warning restore IDE0056 -} diff --git a/Messages/Data/Ean.cs b/Messages/Data/Ean.cs deleted file mode 100644 index 31fd4d90..00000000 --- a/Messages/Data/Ean.cs +++ /dev/null @@ -1,706 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Trivial.Data; - -/// -/// The International Article Number, a.k.a. European Article Number or EAN. -/// -public partial class InternationalArticleNumber -{ - /// - /// All digits. - /// - private string value; - - /// - /// Converts the value of the current object to its equivalent string representation. - /// - /// The string representation of the value of this object, which consists of a sequence of 7 digits of 0 and 1. - public override string ToString() - => value ?? string.Empty; - - /// - /// Gets the checksum of EAN13. - /// - /// The checksum. - /// The digits was not valid. - public byte Checksum() - { - if (string.IsNullOrEmpty(value)) - throw new InvalidOperationException("The value should not be null."); - return Checksum(value); - } - - /// - /// Converts to code list. - /// - /// The code list. - public List ToList() - { - if (value == null) return new(); - return value.Select(ele => ele switch - { - '0' => Zero, - '1' => One, - '2' => Two, - '3' => Three, - '4' => Four, - '5' => Five, - '6' => Six, - '7' => Seven, - '8' => Eight, - '9' => Nine, - _ => null - }).Where(ele => ele != null).ToList(); - } - - /// - /// Returns a string that represents the barcode in stroke path for SVG or XAML. - /// - /// The bar height. - /// A stroke path string that represents the barcode. - public string ToPathString(int height = 40) - { - var sb = new StringBuilder(); - var i = 6; - sb.Append("M0,0 "); - foreach (var b in ToBarcode()) - { - i++; - if (b) sb.Append($"M{i},0 L{i},{height} "); - } - - sb.Append($"M{i + 6},0"); - return sb.ToString(); - } - - /// - /// Converts to boolean list. - /// White represented as false, black represented as true. - /// - /// The boolean list of barcode. - /// It was not an EAN-13 ro EAN-8 code. - public List ToBarcode() - { - var codes = ToList(); - var col = new List(); - if (codes.Count == 13) - { - col.Add(true); - col.Add(false); - col.Add(true); - switch (value.FirstOrDefault()) - { - case '0': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.L)); - col.AddRange(codes[3].ToList(Encodings.L)); - col.AddRange(codes[4].ToList(Encodings.L)); - col.AddRange(codes[5].ToList(Encodings.L)); - col.AddRange(codes[6].ToList(Encodings.L)); - break; - case '1': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.L)); - col.AddRange(codes[3].ToList(Encodings.G)); - col.AddRange(codes[4].ToList(Encodings.L)); - col.AddRange(codes[5].ToList(Encodings.G)); - col.AddRange(codes[6].ToList(Encodings.G)); - break; - case '2': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.L)); - col.AddRange(codes[3].ToList(Encodings.G)); - col.AddRange(codes[4].ToList(Encodings.G)); - col.AddRange(codes[5].ToList(Encodings.L)); - col.AddRange(codes[6].ToList(Encodings.G)); - break; - case '3': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.L)); - col.AddRange(codes[3].ToList(Encodings.G)); - col.AddRange(codes[4].ToList(Encodings.G)); - col.AddRange(codes[5].ToList(Encodings.G)); - col.AddRange(codes[6].ToList(Encodings.L)); - break; - case '4': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.G)); - col.AddRange(codes[3].ToList(Encodings.L)); - col.AddRange(codes[4].ToList(Encodings.L)); - col.AddRange(codes[5].ToList(Encodings.G)); - col.AddRange(codes[6].ToList(Encodings.G)); - break; - case '5': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.G)); - col.AddRange(codes[3].ToList(Encodings.G)); - col.AddRange(codes[4].ToList(Encodings.L)); - col.AddRange(codes[5].ToList(Encodings.L)); - col.AddRange(codes[6].ToList(Encodings.G)); - break; - case '6': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.G)); - col.AddRange(codes[3].ToList(Encodings.G)); - col.AddRange(codes[4].ToList(Encodings.G)); - col.AddRange(codes[5].ToList(Encodings.L)); - col.AddRange(codes[6].ToList(Encodings.L)); - break; - case '7': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.G)); - col.AddRange(codes[3].ToList(Encodings.L)); - col.AddRange(codes[4].ToList(Encodings.G)); - col.AddRange(codes[5].ToList(Encodings.L)); - col.AddRange(codes[6].ToList(Encodings.G)); - break; - case '8': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.G)); - col.AddRange(codes[3].ToList(Encodings.L)); - col.AddRange(codes[4].ToList(Encodings.G)); - col.AddRange(codes[5].ToList(Encodings.G)); - col.AddRange(codes[6].ToList(Encodings.L)); - break; - case '9': - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.G)); - col.AddRange(codes[3].ToList(Encodings.G)); - col.AddRange(codes[4].ToList(Encodings.L)); - col.AddRange(codes[5].ToList(Encodings.G)); - col.AddRange(codes[6].ToList(Encodings.L)); - break; - } - - col.Add(false); - col.Add(true); - col.Add(false); - col.Add(true); - col.Add(false); - col.AddRange(codes[7].ToList(Encodings.R)); - col.AddRange(codes[8].ToList(Encodings.R)); - col.AddRange(codes[9].ToList(Encodings.R)); - col.AddRange(codes[10].ToList(Encodings.R)); - col.AddRange(codes[11].ToList(Encodings.R)); - col.AddRange(codes[12].ToList(Encodings.R)); - col.Add(true); - col.Add(false); - col.Add(true); - return col; - } - else if (codes.Count == 8) - { - col.Add(true); - col.Add(false); - col.Add(true); - col.AddRange(codes[0].ToList(Encodings.L)); - col.AddRange(codes[1].ToList(Encodings.L)); - col.AddRange(codes[2].ToList(Encodings.L)); - col.AddRange(codes[3].ToList(Encodings.L)); - col.Add(false); - col.Add(true); - col.Add(false); - col.Add(true); - col.Add(false); - col.AddRange(codes[4].ToList(Encodings.R)); - col.AddRange(codes[5].ToList(Encodings.R)); - col.AddRange(codes[6].ToList(Encodings.R)); - col.AddRange(codes[7].ToList(Encodings.R)); - col.Add(true); - col.Add(false); - col.Add(true); - return col; - } - else if (codes.Count == 5) - { - var checksum = Checksum(); - col.Add(false); - col.Add(true); - col.Add(false); - col.Add(true); - col.Add(true); - col.AddRange(codes[0].ToList(checksum < 4 ? Encodings.G : Encodings.L)); - col.Add(false); - col.Add(true); - col.AddRange(codes[1].ToList(checksum == 0 || checksum == 4 || checksum == 7 || checksum == 8 ? Encodings.G : Encodings.L)); - col.Add(false); - col.Add(true); - col.AddRange(codes[2].ToList(checksum == 1 || checksum == 4 || checksum == 5 || checksum == 9 ? Encodings.G : Encodings.L)); - col.Add(false); - col.Add(true); - col.AddRange(codes[3].ToList(checksum == 2 || checksum == 5 || checksum == 6 || checksum == 7 ? Encodings.G : Encodings.L)); - col.Add(false); - col.Add(true); - col.AddRange(codes[4].ToList(checksum == 3 || checksum == 6 || checksum == 8 || checksum == 9 ? Encodings.G : Encodings.L)); - return col; - } - else if (codes.Count == 2) - { - var checksum = Checksum(); - col.Add(false); - col.Add(true); - col.Add(false); - col.Add(true); - col.Add(true); - col.AddRange(codes[0].ToList(checksum % 4 < 2 ? Encodings.L : Encodings.G)); - col.Add(false); - col.Add(true); - col.AddRange(codes[1].ToList(checksum % 2 == 0 ? Encodings.L : Encodings.G)); - return col; - } - - throw new InvalidOperationException("The count of digit is not any of 7, 8, 12 or 13."); - } - - /// - /// Converts to a string. - /// - /// The barcode string. - /// It was not an EAN-13 ro EAN-8 code. - public string ToBarcodeString() - => string.Join(string.Empty, ToBarcode().Select(ele => ele ? '1' : '0')); - - /// - /// Converts to a string. - /// - /// The value of black represented. - /// The value of white represented. - /// The barcode string. - /// It was not an EAN-13 ro EAN-8 code. - public string ToBarcodeString(char black, char white) - => string.Join(string.Empty, ToBarcode().Select(ele => ele ? black : white)); - - /// - /// Converts to a string. - /// - /// The selector to convert boolean array to a string. Per boolean value, white represented as false, black represented as true. - /// The barcode string. - /// It was not an EAN-13 ro EAN-8 code. - public string ToBarcodeString(Func selector) - => selector != null ? string.Join(string.Empty, ToBarcode().Select(selector)) : ToBarcodeString(); - - /// - /// Converts to a string. - /// - /// The selector to convert boolean array to a string. Per boolean value, white represented as false, black represented as true. - /// The barcode string. - /// It was not an EAN-13 ro EAN-8 code. - public string ToBarcodeString(Func selector) - => selector != null ? string.Join(string.Empty, ToBarcode().Select(selector)) : ToBarcodeString(); - - /// - /// Creates an EAN. - /// - /// The digits. - /// An instance of the EAN. - /// The input sequence was null. - /// The digits was not valid. - public static InternationalArticleNumber Create(params byte[] digits) - { - if (digits.Length < 6) - { - foreach (var d in digits) - { - if (d > 9) throw new InvalidOperationException("The digit should be less than 10."); - } - - if (digits.Length == 5 || digits.Length == 2) - return new InternationalArticleNumber - { - value = string.Join(string.Empty, digits) - }; - - throw new InvalidOperationException("The count of digit is too less."); - } - - var check = Checksum(digits); - var col = digits.Length switch - { - 13 => digits[12] == check ? digits.Take(12) : null, - 8 => digits[7] == check ? digits.Take(7) : null, - 18 => digits[17] == check ? digits.Take(17) : null, - _ => digits - }; - if (col == null) - throw new InvalidOperationException($"Check failed. Expects {check} but {digits.LastOrDefault()}."); - return new InternationalArticleNumber - { - value = string.Join(string.Empty, col) + check - }; - } - - /// - /// Creates an EAN. - /// - /// The digits. - /// An instance of the EAN. - /// The input sequence was null. - /// The digits was not valid. - public static InternationalArticleNumber Create(IEnumerable digits) - => Create(digits?.ToArray()); - - /// - /// Creates an EAN. - /// - /// The EAN digits; or barcode areas that white represented as 0 and black represented as 1. - /// An instance of the EAN. - /// The input sequence was null. - /// The digits was not valid. - public static InternationalArticleNumber Create(string sequence) - { - if (sequence == null) throw new ArgumentNullException(nameof(sequence), "sequence should not be null."); - sequence = sequence.Trim(); - if (sequence.Length < 21 || sequence.Contains('9') || sequence.Replace("0", string.Empty).Replace("1", string.Empty).Replace("-", string.Empty).Trim().Length > 0) - return Create(ToList(sequence)); - var col = new List(); - foreach (var c in sequence) - { - if (c == '0') col.Add(false); - else if (c == '1') col.Add(true); - } - - return Create(col); - } - - /// - /// Creates an EAN. - /// - /// The barcode. White represented as false, black represented as true. - /// An instance of the EAN. - /// The barcode was null. - /// The barcode was not valid. - public static InternationalArticleNumber Create(IEnumerable barcode) - { -#pragma warning disable IDE0056 - if (barcode == null) throw new ArgumentNullException(nameof(barcode), "barcode should not be null."); - var col = barcode?.ToList(); - var sb = new StringBuilder(); - if (col.Count < 49) - { - if (col.Count != 21 && col.Count != 48) - throw new InvalidOperationException("The count of barcode area is too less."); - if (col[0] || !col[1] || col[2] || !col[3] || !col[4]) - throw new InvalidOperationException("The start marker is not invalid."); - for (var i = 5; i < col.Count; i += 9) - { - var n = GetLeftDigitNumber(col, i, out _); - sb.Append(n); - } - - return Create(sb.ToString()); - } - - if (!col[0] || col[1] || !col[2] || col[3]) - throw new InvalidOperationException("The start marker is not invalid."); - if (!col[col.Count - 1] || col[col.Count - 2] || !col[col.Count - 3] || col[col.Count - 4]) - throw new InvalidOperationException("The end marker is not invalid."); - if (col.Count == 95) - { - { - var n = GetLeftDigitNumber(col, 3, out var e); - if (e == Encodings.G) - { - col.Reverse(); - n = GetLeftDigitNumber(col, 3, out e); - } - - if (e != Encodings.L) throw new InvalidOperationException("The format of the first digit is invalid."); - sb.Append(n); - } - - var left = new List { false }; - for (var i = 10; i < 45; i += 7) - { - var n = GetLeftDigitNumber(col, i, out var e); - left.Add(e == Encodings.G); - sb.Append(n); - } - - sb.Insert(0, GetFirstDigitNumber(left)); - for (var i = 50; i < 92; i += 7) - { - var n = GetRightDigitNumber(col, i); - sb.Append(n); - } - } - else if (col.Count == 67) - { - { - var n = GetLeftDigitNumber(col, 3, out var e); - if (e == Encodings.G) - { - col.Reverse(); - n = GetLeftDigitNumber(col, 3, out e); - } - - if (e != Encodings.L) throw new InvalidOperationException("The format of the first digit is invalid."); - sb.Append(n); - } - - for (var i = 10; i < 31; i += 7) - { - var n = GetLeftDigitNumber(col, i, out _); - sb.Append(n); - } - - for (var i = 36; i < 64; i += 7) - { - var n = GetRightDigitNumber(col, i); - sb.Append(n); - } - } - else if (col.Count == 84) - { - { - var n = GetLeftDigitNumber(col, 0, out var e); - if (e == Encodings.G) - { - col.Reverse(); - n = GetLeftDigitNumber(col, 0, out e); - } - - if (e != Encodings.L) throw new InvalidOperationException("The format of the first digit is invalid."); - sb.Append(n); - } - - var left = new List { false }; - for (var i = 7; i < 42; i += 7) - { - var n = GetLeftDigitNumber(col, i, out var e); - left.Add(e == Encodings.G); - sb.Append(n); - } - - sb.Insert(0, GetFirstDigitNumber(left)); - for (var i = 42; i < 84; i += 7) - { - var n = GetRightDigitNumber(col, i); - sb.Append(n); - } - } - else - { - throw new InvalidOperationException("The count of barcode is invalid. Should be 95 or 67."); - } - - return Create(sb.ToString()); -#pragma warning restore IDE0056 - } - - /// - /// Gets the checksum. - /// - /// The digits. - /// The checksum. - /// The input sequence was null. - /// The digits was not valid. - public static byte Checksum(params byte[] digits) - { - if (digits == null) throw new ArgumentNullException(nameof(digits), "digits should not be null."); - foreach (var d in digits) - { - if (d > 9) throw new InvalidOperationException("The digit should be less than 10."); - } - - return (byte)(digits.Length switch - { - 12 or 13 => (10 - ((digits[0] + digits[2] + digits[4] + digits[6] + digits[8] + digits[10] + (digits[1] + digits[3] + digits[5] + digits[7] + digits[9] + digits[11]) * 3) % 10)) % 10, - 7 or 8 => (10 - ((digits[1] + digits[3] + digits[5] + (digits[0] + digits[2] + digits[4] + digits[6]) * 3) % 10)) % 10, - 17 or 18 => (10 - ((digits[1] + digits[3] + digits[5] + digits[7] + digits[9] + digits[11] + digits[13] + digits[15] + (digits[0] + digits[2] + digits[4] + digits[6] + digits[8] + digits[10] + digits[12] + digits[14] + digits[16]) * 3) % 10)) % 10, - 5 => ((digits[0] + digits[2] + digits[4]) * 3 + (digits[1] + digits[3]) * 9) % 10, - 2 => (digits[0] * 2 + digits[1]) % 4, - _ => throw new InvalidOperationException("The count of digit is not any of 7, 8, 12 or 13.") - }); - } - - /// - /// Gets the checksum. - /// - /// The digits. - /// The checksum. - /// The input sequence was null. - /// The digits was not valid. - public static byte Checksum(IEnumerable digits) - => Checksum(digits?.ToArray()); - - /// - /// Gets the checksum. - /// - /// The EAN digits. - /// The checksum. - /// The input sequence was null. - /// The input sequence was not valid. - public static byte Checksum(string sequence) - => Checksum(ToList(sequence)); - - /// - /// Validates the checksum. - /// - /// The digits. - /// true if checksum is correct; otherwise, false. - public static bool Validate(params byte[] digits) - => digits != null && digits.Length switch - { - 13 => (10 - ((digits[0] + digits[2] + digits[4] + digits[6] + digits[8] + digits[10] + (digits[1] + digits[3] + digits[5] + digits[7] + digits[9] + digits[11]) * 3) % 10)) % 10 == digits[12], - 8 => (10 - ((digits[1] + digits[3] + digits[5] + (digits[0] + digits[2] + digits[4] + digits[6]) * 3) % 10)) % 10 == digits[7], - 18 => (10 - ((digits[1] + digits[3] + digits[5] + digits[7] + digits[9] + digits[11] + digits[13] + digits[15] + (digits[0] + digits[2] + digits[4] + digits[6] + digits[8] + digits[10] + digits[12] + digits[14] + digits[16]) * 3) % 10)) % 10 == digits[17], - 5 or 2 => true, - _ => false - }; - - /// - /// Validates the checksum. - /// - /// The digits. - /// true if checksum is correct; otherwise, false. - public static bool Validate(IEnumerable digits) - => Validate(digits?.ToArray()); - - /// - /// Validates the checksum. - /// - /// The EAN digits. - /// true if checksum is correct; otherwise, false. - public static bool Validate(string sequence) - { - if (string.IsNullOrEmpty(sequence)) return false; - try - { - return Validate(ToList(sequence)); - } - catch (InvalidOperationException) - { - } - - return false; - } - - private static byte GetFirstDigitNumber(List col) - => string.Join(string.Empty, col.Select(ele => ele ? 'G' : 'L')) switch - { - "LLLLLL" => 0, - "LLGLGG" => 1, - "LLGGLG" => 2, - "LLGGGL" => 3, - "LGLLGG" => 4, - "LGGLLG" => 5, - "LGGGLL" => 6, - "LGLGLG" => 7, - "LGLGGL" => 8, - "LGGLGL" => 9, - _ => throw new InvalidOperationException("The encoding of first group is invalid.") - }; - - private static byte GetLeftDigitNumber(List col, int i, out Encodings encoding) - { - if (col[i]) throw new InvalidOperationException($"Position {i} should be white (false or 0) but is black in fact."); - if (!col[i + 6]) throw new InvalidOperationException($"Position {i + 6} should be black (true or 1) but is white in fact."); - var b = (byte)(0b1 - + (col[i + 1] ? 0b100000 : 0) - + (col[i + 2] ? 0b10000 : 0) - + (col[i + 3] ? 0b1000 : 0) - + (col[i + 4] ? 0b100 : 0) - + (col[i + 5] ? 0b10 : 0)); - try - { - return GetDigitNumber(b, out encoding); - } - catch (InvalidOperationException) - { - throw new InvalidOperationException($"Code {Code.ToString(b)} at position {i} is not a valid digit."); - } - } - - private static byte GetRightDigitNumber(List col, int i) - { - if (!col[i]) throw new InvalidOperationException($"Position {i} should be black (true or 1) but is white in fact."); - if (col[i + 6]) throw new InvalidOperationException($"Position {i + 6} should be white (false or 0) but is black in fact."); - var b = (byte)(0b1000000 - + (col[i + 1] ? 0b100000 : 0) - + (col[i + 2] ? 0b10000 : 0) - + (col[i + 3] ? 0b1000 : 0) - + (col[i + 4] ? 0b100 : 0) - + (col[i + 5] ? 0b10 : 0)); - byte r; - Encodings encoding; - try - { - r = GetDigitNumber(b, out encoding); - } - catch (InvalidOperationException) - { - throw new InvalidOperationException($"Code {Code.ToString(b)} at position {i} is not a valid digit."); - } - - if (encoding != Encodings.R) - throw new InvalidOperationException($"Code {Code.ToString(b)} ({r}) at position {i} is not R-column."); - return r; - } - - /// - /// Creates a byte list. - /// - /// The EAN digits. - /// The byte list. - /// The input sequence was null. - /// The input sequence was not valid. - private static List ToList(string sequence) - { - if (sequence == null) throw new ArgumentNullException(nameof(sequence), "sequence should not be null."); - var arr = new List(); - var i = -1; - foreach (var c in sequence) - { - i++; - switch (c) - { - case '-': - case ' ': - case '(': - case ')': - continue; - case '0': - arr.Add(0); - break; - case '1': - arr.Add(1); - break; - case '2': - arr.Add(2); - break; - case '3': - arr.Add(3); - break; - case '4': - arr.Add(4); - break; - case '5': - arr.Add(5); - break; - case '6': - arr.Add(6); - break; - case '7': - arr.Add(7); - break; - case '8': - arr.Add(8); - break; - case '9': - arr.Add(9); - break; - default: - throw new InvalidOperationException( - $"The input sequence is not valid. Character at index {i} is {c} but expects a digit.", - new ArgumentException("sequence format is not valid.", nameof(sequence))); - } - } - - return arr; - } -} diff --git a/Messages/Data/EanCli.cs b/Messages/Data/EanCli.cs deleted file mode 100644 index f4eeef4d..00000000 --- a/Messages/Data/EanCli.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Trivial.Collection; -using Trivial.CommandLine; - -namespace Trivial.Data; - -/// -/// The International Article Number, a.k.a. European Article Number or EAN. -/// -public partial class InternationalArticleNumber -{ - /// - /// Converts to boolean list and writes to the standard output stream. - /// White represented as false, black represented as true. - /// - /// The command line interface proxy. - /// The style that foreground represents black and background represents white. - /// The boolean list. - /// It was not an EAN-13 ro EAN-8 code. - public List ToBarcode(StyleConsole cli, ConsoleTextStyle style = null) - { - List barcode; - try - { - barcode = ToBarcode(); - } - catch (InvalidOperationException) - { - cli.WriteLine(style, value); - throw; - } - - var col = new List(); - if (style == null) style = new ConsoleTextStyle(System.Drawing.Color.FromArgb(16, 16, 16), ConsoleColor.Black, System.Drawing.Color.FromArgb(206, 206, 206), ConsoleColor.Gray); - var black = new ConsoleTextStyle(style.ForegroundRgbColor, style.ForegroundConsoleColor, style.ForegroundRgbColor, style.ForegroundConsoleColor); - var white = new ConsoleTextStyle(style.BackgroundRgbColor, style.BackgroundConsoleColor, style.BackgroundRgbColor, style.BackgroundConsoleColor); - var bg = new string(' ', 12 + barcode.Count); - col.Add(bg, white); - col.Add(Environment.NewLine); - var s = value; - cli.Flush(); - if (cli.Mode == StyleConsole.Modes.Text && cli.Handler == null) - { - cli.WriteLine(style, s); - return barcode; - } - else if (barcode.Count + 12 > GetBufferSafeWidth(cli)) - { - col.Clear(); - foreach (var b in barcode) - { - col.Add(' ', 1, b ? black : white); - } - - col.Add(Environment.NewLine); - col.Add(s, style); - cli.WriteLine(col); - return barcode; - } - else if (barcode.Count == 95 && s.Length == 13) - { - for (var i = 0; i < 4; i++) - { - col.Add(' ', 7, white); - foreach (var b in barcode) - { - col.Add(' ', 1, b ? black : white); - } - - col.Add(' ', 5, white); - col.Add(Environment.NewLine); - } - - var isbn = false; - if (s.StartsWith("97")) - { - if (s.StartsWith("9790")) - { - isbn = true; - col.Add("ISMN 9 ", style); - } - else if (s.StartsWith("978") || s.StartsWith("979")) - { - isbn = true; - col.Add("ISBN 9 ", style); - } - else if (s.StartsWith("977")) - { - isbn = true; - col.Add("ISSN 9 ", style); - } - } - - if (!isbn) - { - col.Add(' ', 4, white); - col.Add(s[0], 1, style); - col.Add(' ', 2, white); - } - - for (var i = 0; i < 3; i++) - { - col.Add(' ', 1, barcode[i] ? black : white); - } - - for (var i = 1; i < 7; i++) - { - col.Add(' ', 3, white); - col.Add(s[i], 1, style); - col.Add(' ', 3, white); - } - - for (var i = 45; i < 50; i++) - { - col.Add(' ', 1, barcode[i] ? black : white); - } - - for (var i = 7; i < 13; i++) - { - col.Add(' ', 3, white); - col.Add(s[i], 1, style); - col.Add(' ', 3, white); - } - - for (var i = 92; i < 95; i++) - { - col.Add(' ', 1, barcode[i] ? black : white); - } - - col.Add(' ', 5, white); - col.Add(Environment.NewLine); - } - else if (barcode.Count == 67 && s.Length == 8) - { - for (var i = 0; i < 4; i++) - { - col.Add(' ', 6, white); - foreach (var b in barcode) - { - col.Add(' ', 1, b ? black : white); - } - - col.Add(' ', 6, white); - col.Add(Environment.NewLine); - } - - col.Add(' ', 6, white); - for (var i = 0; i < 3; i++) - { - col.Add(' ', 1, barcode[i] ? black : white); - } - - for (var i = 0; i < 4; i++) - { - col.Add(' ', 3, white); - col.Add(s[i], 1, style); - col.Add(' ', 3, white); - } - - for (var i = 31; i < 36; i++) - { - col.Add(' ', 1, barcode[i] ? black : white); - } - - for (var i = 4; i < 8; i++) - { - col.Add(' ', 3, white); - col.Add(s[i], 1, style); - col.Add(' ', 3, white); - } - - for (var i = 64; i < 67; i++) - { - col.Add(' ', 1, barcode[i] ? black : white); - } - - col.Add(' ', 6, white); - col.Add(Environment.NewLine); - } - else if ((barcode.Count == 48 && s.Length == 5) || (barcode.Count == 21 && s.Length == 2)) - { - col.Add(' ', 14, white); - foreach (var c in s) - { - col.Add(c, 1, style); - col.Add(' ', 8, white); - } - - col.Add(' ', 1, white); - col.Add(Environment.NewLine); - for (var i = 0; i < 4; i++) - { - col.Add(' ', 6, white); - foreach (var b in barcode) - { - col.Add(' ', 1, b ? black : white); - } - - col.Add(' ', 6, white); - col.Add(Environment.NewLine); - } - } - else - { - for (var i = 0; i < 4; i++) - { - col.Add(' ', 6, white); - foreach (var b in barcode) - { - col.Add(' ', 1, b ? black : white); - } - - col.Add(' ', 6, white); - col.Add(Environment.NewLine); - } - } - - col.Add(bg, white); - (cli ?? StyleConsole.Default).WriteLine(col); - return barcode; - } - - private static int GetBufferSafeWidth(StyleConsole cli) - { - try - { - return (cli ?? StyleConsole.Default).BufferWidth - 1; - } - catch (System.IO.IOException) - { - } - catch (InvalidOperationException) - { - } - catch (NotSupportedException) - { - } - catch (System.Security.SecurityException) - { - } - catch (System.Runtime.InteropServices.ExternalException) - { - } - catch (ArgumentException) - { - } - - return 70; - } -} diff --git a/Messages/Data/EanCode.cs b/Messages/Data/EanCode.cs deleted file mode 100644 index 1eae7942..00000000 --- a/Messages/Data/EanCode.cs +++ /dev/null @@ -1,415 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Trivial.Data; - -/// -/// The International Article Number, a.k.a. European Article Number or EAN. -/// -public partial class InternationalArticleNumber -{ - /// - /// The encoding for Code. - /// - public enum Encodings : byte - { - /// - /// Odd parity for first group. - /// - L = 1, - - /// - /// Even parity for first group. - /// - G = 2, - - /// - /// Last group. - /// - R = 3 - } - - /// - /// The code of a Code. - /// - public class Code : IEquatable, IEquatable - { - /// - /// Initializes a new instance of the InternationalArticleNumber.Code class. - /// - /// The code. - public Code(byte code) - { - int r; - int rg; - if (code > 0b111111) - { - if (code > 0b1111111) - R = (byte)(code >> 1); - else - R = code; - rg = ((~code) + 128) % 128; - L = (byte)(rg); - r = code; - } - else - { - L = code; - r = ((~code) + 128) % 128; - R = (byte)r; - rg = code; - } - - G = Reverse(r); - RG = Reverse(rg); - } - - /// - /// Gets the binary (last 7 bits) of the Code encoded using odd parity for first group. - /// - public byte L { get; } - - /// - /// Gets the binary (last 7 bits) of the Code encoded using even parity for first group. - /// - public byte G { get; } - - /// - /// Gets the binary (last 7 bits) of the Code encoded for last group. - /// - public byte R { get; } - - internal byte RG { get; } - - /// - /// Gets the value. - /// - /// The encoding. - /// The value in byte (last 7 bit). - /// encoding is not valid. - public byte Get(Encodings encoding) - => encoding switch - { - Encodings.L => L, - Encodings.R => R, - Encodings.G => G, - (Encodings)4 => RG, - _ => throw new InvalidOperationException("encoding is invalid.", new ArgumentOutOfRangeException(nameof(encoding), "encoding is not the one of supported.")) - }; - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - public bool Equals(byte other) - { - return other == L || other == R || other == G || other == RG; - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - public bool Equals(Code other) - { - return other != null && L == other.L && G == other.G; - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - public override bool Equals(object obj) - { - if (obj is null) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj is byte b) return Equals(b); - return Equals(obj as Code); - } - - /// - public override int GetHashCode() - => L.GetHashCode(); - - /// - /// Gets the encoding if the specific value is the current object; otherwise, 0. - /// - /// An object to compare with this object. - /// The enocding used in this object. - public Encodings GetEncoding(byte other) - { - if (other == L) return Encodings.L; - if (other == R) return Encodings.R; - if (other == G) return Encodings.G; - if (other == RG) return (Encodings)4; - return 0; - } - - /// - /// Converts to a list of boolean about areas. - /// - /// The encoding. - /// The areas. White represented as false, black represented as true. - /// encoding is not valid. - public List ToList(Encodings encoding) - => encoding switch - { - Encodings.L => ToList(L), - Encodings.R => ToList(R), - Encodings.G => ToList(G), - (Encodings)4 => ToList(RG), - _ => throw new InvalidOperationException("encoding is invalid.", new ArgumentOutOfRangeException(nameof(encoding), "encoding is not the one of supported.")) - }; - - /// - /// Converts to a list of boolean. - /// - /// The encoding. - /// The boolean array. - /// encoding is not valid. - public string ToString(Encodings encoding) - => encoding switch - { - Encodings.L => ToString(L), - Encodings.R => ToString(R), - Encodings.G => ToString(G), - (Encodings)4 => ToString(RG), - _ => throw new InvalidOperationException("encoding is invalid.", new ArgumentOutOfRangeException(nameof(encoding), "encoding is not the one of supported.")) - }; - - /// - /// Converts the value of the current object to its equivalent string representation. - /// - /// The string representation of the value of this object, which consists of a sequence of 7 digits of 0 and 1. - public override string ToString() - { - return ToString(Encodings.L); - } - - internal static string ToString(byte value) - { - var s = Maths.Numbers.ToPositionalNotationString((long)value, 2); - return s.Length < 7 ? s.PadLeft(7 - s.Length, '0') : s; - } - - private static List ToList(int value) - => new() - { - (value & 0b1000000) > 0, - (value & 0b100000) > 0, - (value & 0b10000) > 0, - (value & 0b1000) > 0, - (value & 0b100) > 0, - (value & 0b10) > 0, - (value & 0b1) > 0, - }; - - private static byte Reverse(int value) - => (byte)(((value & 0b1000000) > 0 ? 0b1 : 0) - + ((value & 0b100000) > 0 ? 0b10 : 0) - + ((value & 0b10000) > 0 ? 0b100 : 0) - + ((value & 0b1000) > 0 ? 0b1000 : 0) - + ((value & 0b100) > 0 ? 0b10000 : 0) - + ((value & 0b10) > 0 ? 0b100000 : 0) - + ((value & 0b1) > 0 ? 0b1000000 : 0)); - } - - /// - /// Gets the Code 0. - /// - public static Code Zero { get; } = new(0b0001101); - - /// - /// Gets the Code 1. - /// - public static Code One { get; } = new(0b0011001); - - /// - /// Gets the Code 2. - /// - public static Code Two { get; } = new(0b0010011); - - /// - /// Gets the Code 3. - /// - public static Code Three { get; } = new(0b0111101); - - /// - /// Gets the Code 4. - /// - public static Code Four { get; } = new(0b0100011); - - /// - /// Gets the Code 5. - /// - public static Code Five { get; } = new(0b0110001); - - /// - /// Gets the Code 6. - /// - public static Code Six { get; } = new(0b0101111); - - /// - /// Gets the Code 7. - /// - public static Code Seven { get; } = new(0b0111011); - - /// - /// Gets the Code 8. - /// - public static Code Eight { get; } = new(0b0110111); - - /// - /// Gets the Code 9. - /// - public static Code Nine { get; } = new(0b0001011); - - /// - /// Gets the digit code. - /// - /// The number from 0 to 9. - /// The Code. - /// The number was negative or greater than 9. - public static Code GetDigitCode(byte number) - { - return number switch - { - 0 => Zero, - 1 => One, - 2 => Two, - 3 => Three, - 4 => Four, - 5 => Five, - 6 => Six, - 7 => Seven, - 9 => Eight, - _ => throw (number > 10 - ? new ArgumentOutOfRangeException(nameof(number), "number is out of range because it is greater than 9.") - : new ArgumentOutOfRangeException(nameof(number), "number is out of range because it is negative.")) - }; - } - - /// - /// Gets the digit number - /// - /// The code in byte (last 7 bits). - /// The encoding. - /// The digit number. - /// The code is not mapped to a digit. - public static byte GetDigitNumber(byte code, out Encodings encoding) - { - var e = Zero.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 0; - } - - e = One.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 1; - } - - e = Two.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 2; - } - - e = Three.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 3; - } - - e = Four.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 4; - } - - e = Five.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 5; - } - - e = Six.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 6; - } - - e = Seven.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 7; - } - - e = Eight.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 8; - } - - e = Nine.GetEncoding(code); - if (e > 0) - { - encoding = e; - return 9; - } - - throw new InvalidOperationException("The code was invalid since it does not mapped to a digit."); - } - - /// - /// Gets the digit number - /// - /// The code in byte (last 7 bits). - /// The digit number. - public static byte GetDigitNumber(byte code) - => GetDigitNumber(code, out _); - - /// - /// Gets the digit number - /// - /// The code in byte (last 7 bits). - /// The digit number. - /// The code is not mapped to a digit. - public static byte GetDigitNumber(Code code) - { - if (Zero.Equals(code)) return 0; - if (One.Equals(code)) return 1; - if (Two.Equals(code)) return 2; - if (Three.Equals(code)) return 3; - if (Four.Equals(code)) return 4; - if (Five.Equals(code)) return 5; - if (Six.Equals(code)) return 6; - if (Seven.Equals(code)) return 7; - if (Eight.Equals(code)) return 8; - if (Nine.Equals(code)) return 9; - throw new InvalidOperationException("The code was invalid since it does not mapped to a digit."); - } -} diff --git a/Messages/Data/ResourceEntityInfo.cs b/Messages/Data/ResourceEntityInfo.cs deleted file mode 100644 index e33c9dfb..00000000 --- a/Messages/Data/ResourceEntityInfo.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using System.Xml.Linq; -using Trivial.Reflection; -using Trivial.Text; - -namespace Trivial.Data; - -/// -/// The entity information. -/// -public abstract class BaseResourceEntityInfo : BaseObservableProperties, IJsonObjectHost -{ - /// - /// Initializes a new instance of the BaseResourceObservableProperties class. - /// - public BaseResourceEntityInfo() - { - } - - /// - /// Initializes a new instance of the BaseResourceObservableProperties class. - /// - /// The identifier. - public BaseResourceEntityInfo(Guid id) - : this(id.ToString("N")) - { - } - - /// - /// Initializes a new instance of the BaseResourceObservableProperties class. - /// - /// The identifier. - public BaseResourceEntityInfo(string id) - { - Id = id; - } - - /// - /// Gets or sets the identifier. - /// - [DataMember(Name = "id")] - [JsonPropertyName("id")] - public string Id - { - get => GetCurrentProperty(); - protected set => SetCurrentProperty(value); - } - - /// - /// Converts to JSON object. - /// - /// A JSON object. - public virtual JsonObjectNode ToJson() - => new() - { - { "id", Id }, - }; - - /// - /// Gets a property value. - /// - /// The type of the property value. - /// The key. - /// The default value. - /// A property value. - public new T GetProperty(string key, T defaultValue = default) - => base.GetProperty(key, defaultValue); - - /// - /// Gets a property value. - /// - /// The type of the property value. - /// The key. - /// The property value. - /// true if contains; otherwise, false. - public new bool GetProperty(string key, out T result) - => base.GetProperty(key, out result); -} diff --git a/Messages/IO/FileAssociationInfo.cs b/Messages/IO/FileAssociationInfo.cs deleted file mode 100644 index f6883021..00000000 --- a/Messages/IO/FileAssociationInfo.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; - -namespace Trivial.IO; - -/// -/// The file association information. -/// -[DataContract] -public class FileAssociationInfo -{ - /// - /// Gets or sets the file extension part. - /// - [JsonPropertyName("ext")] - [DataMember(Name = "ext")] - public string FileExtension { get; set; } - - /// - /// Gets or sets the MIME content type. - /// - [JsonPropertyName("mime")] - [DataMember(Name = "mime")] - public string ContentType { get; set; } - - /// - /// Gets or sets the default icon path. - /// - [JsonPropertyName("icon")] - [DataMember(Name = "icon")] - public string Icon { get; set; } - - /// - /// Ges or sets the friendly name of the content type. - /// - [JsonPropertyName("name")] - [DataMember(Name = "name")] - public string Name { get; set; } - - /// - /// Gets or sets the defaul command. - /// - [JsonPropertyName("defaultcmd")] - [DataMember(Name = "defaultcmd")] - public FileOpenCommandInfo DefaultCommand { get; set; } - - /// - /// Gets or sets the commands. - /// - [JsonPropertyName("cmds")] - [DataMember(Name = "cmds")] - public List Commands { get; set; } = new(); -} - -/// -/// The file open command information. -/// -[DataContract] -public class FileOpenCommandInfo -{ - /// - /// Gets or sets the command key. - /// - [JsonPropertyName("key")] - [DataMember(Name = "key")] - public string Key { get; set; } = "Open"; - - /// - /// Gets or sets the command. - /// - [JsonPropertyName("cmd")] - [DataMember(Name = "cmd")] - public string Command { get; set; } - - /// - /// Ges or sets the friendly name of the command. - /// - [JsonPropertyName("name")] - [DataMember(Name = "name")] - public string Name { get; set; } -} diff --git a/Messages/Maths/BitgNumeralBitmap.cs b/Messages/Maths/BitgNumeralBitmap.cs deleted file mode 100644 index de868f2f..00000000 --- a/Messages/Maths/BitgNumeralBitmap.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using Trivial.Reflection; - -namespace Trivial.Maths; - -/// -/// The numeral bitmap collection of Beacon in the Galaxy. -/// -public static class BitgNumeralBitmap -{ - /// - /// The bitmap width. - /// - public const int WIDTH = 5; - - /// - /// The bitmap height. - /// - public const int HEIGHT = 7; - - /// - /// The radix. - /// - public const int RADIX = 10; - - /// - /// Gets the boolean array of zero. - /// - public readonly static bool[] Zero = new[] { false, false, true, false, false, false, false, true, false, false, true, true, true, true, true, true, false, false,false, false, true, true, true, true, true, true, false, false, true, false, false, false, false, true, false, false }; - - /// - /// Gets the boolean array of one. - /// - public readonly static bool[] One = new[] { true, true, true, true, true, true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true, false, false, false, false }; - - /// - /// Gets the boolean array of two. - /// - public readonly static bool[] Two = new[] { true, true, true, false, false, false, false, true, false, false, true, false, true, false, false, true, false, false ,false, false, true, false, true, true, true, true, false, false, false, false, true, true, true, false, false }; - - /// - /// Gets the boolean array of three. - /// - public readonly static bool[] Three = new[] { true, true, true, true, true, true, false, false, false, false, true, true, true, true, true, true, false, false, false, false, true, false, true, true, true, true, false, false, false, false, true, false, false, false, false}; - - /// - /// Gets the boolean array of four. - /// - public readonly static bool[] Four = new[] { true, false, true, true, true, true, false, false, false, false, true, true, true, false, false, true, false, false, false, false, true, true, true, false, false, true, false, false, false, false, true, true, true, false, false }; - - /// - /// Gets the boolean array of five. - /// - public readonly static bool[] Five = new[] { true, true, true, false, true, true, true, true, false, true, true, false, true, false, true, true, false, false, false, false, true, true, true, false, false, false, false, true, false, false, true, true, true, true, true }; - - /// - /// Gets the boolean array of six. - /// - public readonly static bool[] Six = new[] { false, false, false, false, true, false, true, false, true, true, true, true, true, false, true, false, false, true, false, false, true, true, true, false, false, true, false, true, false, false, true, true, true, false, false }; - - /// - /// Gets the boolean array of seven. - /// - public readonly static bool[] Seven = new[] { true, true, true, false, true, false, false, true, true, true, false, false, true, false, true, false, true, false, false, false, true, false, false, false, false, true, false, false, false, false, true, false, false, false, false }; - - /// - /// Gets the boolean array of eight. - /// - public readonly static bool[] Eight = new[] { true, false, true, true, true, true, true, false, false, false, true, true, true, false, false, true, false, true, false, false, true, true, true, true, true, true, false, true, false, false, true, true, true, true, true }; - - /// - /// Gets the boolean array of nine. - /// - public readonly static bool[] Nine = new[] { false, false, false, false, true, false, true, false, false, true, true, false, true, false, true, true, false, true, false, true, true, true, true, true, true, false, false, true, false, false, true, true, true, false, false }; - - /// - /// Gets the boolean array of equal sign. - /// - public readonly static bool[] EqualSign = new[] { false, false, true, true, true, false, false, true, false, false, true, true, true, true, true, false, false, true, false, false, true, true, true, true, true, false, false, true, false, false, true, true, true, false, false }; - - /// - /// Gets the boolean array of negative sign. - /// - public readonly static bool[] NegativeSign = new[] { false, false, true, true, true, false, false, true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true, true, true }; - - /// - /// Gets the boolean array of plus sign. - /// - public readonly static bool[] PlusSign = new[] { true, false, true, true, true, true, false, false, false, false, true, false, true, false, false, true, true, true, true, false, true, false, true, false, false, true, false, false, false, false, true, false, true, false, false }; - - /// - /// Gets the boolean array of equal sign. - /// - public readonly static bool[] MinusSign = new[] { true, true, true, true, true, false, true, false, true, false, true, false, false, false, false, true, false, false, false, false, true, true, true, true, true, true, false, false, false, false, true, false, false, false, false }; - - /// - /// Gets the boolean array of dot. - /// - public readonly static bool[] Dot = new[] { false, false, true, true, true, false, false, false, false, true, false, false, false, false, true, false, true, false, false, false, true, false, false, false, false, true, false, false, false, false, true, true, true, false, false }; -} diff --git a/Messages/Messages.csproj b/Messages/Messages.csproj deleted file mode 100644 index cd58b0b7..00000000 --- a/Messages/Messages.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - net8.0;net6.0;net48;net462;net461 - Trivial.Messages - Trivial - Trivial.Messages - The library with model of users and messages. - true - true - snupkg - messages.png - https://github.com/nuscien/trivial/raw/master/Materials/logo.png - users messages gpt - - - - - - - - ..\bin\$(Configuration)\ - ..\bin\$(Configuration)\$(TargetFramework)\Trivial.Messages.xml - - - - - - - diff --git a/Messages/README.md b/Messages/README.md deleted file mode 100644 index 9ef97b32..00000000 --- a/Messages/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# [Trivial.Messages](https://trivial.kingcean.net/messages) - -Models of users and messages. - -## Import - -Add following namespace to your code file to use. - -```csharp -using Trivial.Tasks; -using Trivial.Text; -using Trivial.Users; -``` - -## Message models - -- `ExtendedChatMessage` - -## User models - -Get MIME by a specific file extension name. - -- `UserItemInfo` - -## ChatBot - -- `BaseChatCommandGuidanceClient` -- `BaseChatCommandGuidanceEngine` diff --git a/Messages/Tasks/ChatCommandGuidanceClient.cs b/Messages/Tasks/ChatCommandGuidanceClient.cs deleted file mode 100644 index 2b6038c1..00000000 --- a/Messages/Tasks/ChatCommandGuidanceClient.cs +++ /dev/null @@ -1,437 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Nodes; -using System.Threading.Tasks; -using Trivial.Collection; -using Trivial.Data; -using Trivial.Users; -using Trivial.Text; -using System.Threading; -using System.Net.NetworkInformation; -using Trivial.Security; -using Trivial.Net; -using System.Net.Http; - -namespace Trivial.Tasks; - -/// -/// Creates a request. -/// -/// The message text. -/// The message data. -/// The response to reply. -/// The optional cancellation token. -/// The request instance. -public delegate Task ChatCommandGuidanceRequestSend(string message, JsonObjectNode data, ChatCommandGuidanceResponse response, CancellationToken cancellationToken); - -/// -/// The command guidance client for chat bot. -/// -public abstract class BaseChatCommandGuidanceClient -{ - private readonly Dictionary commands = new(); - internal readonly List history = new(); - - /// - /// Initializes a new instance of the BaseChatCommandGuidanceClient class. - /// - /// The user info. - protected BaseChatCommandGuidanceClient(UserItemInfo user) - { - User = user ?? new(); - History = history.AsReadOnly(); - } - - /// - /// Adds or removes the event on message is sending. - /// - public event EventHandler Sending; - - /// - /// Adds or removes the event on message is failed to send or get response. - /// - public event EventHandler> SendFailed; - - /// - /// Adds or removes the event on message is received. - /// - public event EventHandler Received; - - /// - /// Adds or removes the event on message is post processed. - /// - public event EventHandler Processed; - - /// - /// Adds or removes the event on the new topic is created. - /// - public event DataEventHandler NewTopicCreated; - - /// - /// Gets the additional information data. - /// - public JsonObjectNode Info { get; } = new(); - - /// - /// Gets or sets the user info. - /// - public UserItemInfo User { get; } - - /// - /// Gets the command collection. - /// - public ICollection Commands => commands.Values; - - /// - /// Gets the collection of all command key registered. - /// - public ICollection CommandKeys => commands.Keys; - - /// - /// Gets the chat history. - /// - public IList History { get; } - - /// - /// Gets the default message kind of request. - /// - protected internal virtual string RequestMessageKind { get; } = "user"; - - /// - /// Registers a command. - /// - /// The type of the command guidance. - public void Register() where T : BaseChatCommandGuidanceProvider - => Register(Activator.CreateInstance()); - - /// - /// Registers a command. - /// - /// The command key to use. - /// The type of the command guidance. - public void Register(string key) where T : BaseChatCommandGuidanceProvider - => Register(key, Activator.CreateInstance()); - - /// - /// Registers a command. - /// - /// The command. - public void Register(BaseChatCommandGuidanceProvider command) - => Register(command.Command, command); - - /// - /// Registers a command. - /// - /// The command key to use. - /// The command. - public void Register(string key, BaseChatCommandGuidanceProvider command) - { - key ??= command?.Command; - if (key == null) return; - key = key.Trim().ToLowerInvariant(); - if (string.IsNullOrEmpty(key) || key.Length < 2) return; - if (command == null) - { - commands.Remove(key); - return; - } - - commands[key] = command; - command.OnInit(this); - } - - /// - /// Tries to get the specific command. - /// - /// The command key. - /// The command guidance - public BaseChatCommandGuidanceProvider GetCommand(string key) - { - key = key?.Trim()?.ToLowerInvariant(); - if (string.IsNullOrEmpty(key)) return null; - return commands.TryGetValue(key, out var result) ? result : null; - } - - /// - /// Removes a specific command. - /// - /// The command key. - /// true if remove succeeded; otherwise, false. - public bool RemoveCommand(string key) - => commands.Remove(key); - - /// - /// Projects each element of a sequence into a new form by incorporating the element's key. - /// - /// The type of the value returned by selector. - /// A transform function to apply to each source element; the second parameter of the function represents the index of the source element. - /// A collection whose elements are the result of invoking the transform function on each element of source. - public IEnumerable SelectCommand(Func select) - { - if (select == null) yield break; - foreach (var kvp in commands) - { - if (kvp.Value == null) continue; - yield return select(kvp.Key, kvp.Value); - } - } - - /// - /// Projects each element of a sequence into a new form by incorporating the element's key. - /// - /// The type of the value returned by selector. - /// A filter by command guidance kind. - /// A transform function to apply to each source element; the second parameter of the function represents the index of the source element. - /// A collection whose elements are the result of invoking the transform function on each element of source. - public IEnumerable SelectCommand(ChatCommandGuidanceProviderKinds kind, Func select) - { - if (select == null) yield break; - foreach (var kvp in commands) - { - if (kvp.Value?.Kind != kind) continue; - yield return select(kvp.Key, kvp.Value); - } - } - - /// - /// Creates a new topic. - /// - /// The topic instance. - public ChatCommandGuidanceTopic NewTopic() - { - var topic = new ChatCommandGuidanceTopic(this); - NewTopicCreated?.Invoke(this, new DataEventArgs(topic)); - return topic; - } - - /// - /// Occurs on request creates. - /// - /// The arguments. - protected internal virtual void OnRequestCreate(ChatCommandGuidanceMessageEventArgs args) - { - } - - /// - /// Occurs on request is sending. - /// - /// The arguments. - protected internal virtual void OnSend(ChatCommandGuidanceMessageEventArgs args) - { - } - - /// - /// Occurs on response is received. - /// - /// The arguments. - protected internal virtual void OnReceive(ChatCommandGuidanceMessageEventArgs args) - { - } - - /// - /// Occurs on response is processed. - /// - /// The arguments. - protected internal virtual void OnProcessed(ChatCommandGuidanceMessageEventArgs args) - { - } - - /// - /// Sends a message. - /// - /// The request message. - /// The optional cancellation token. - /// The response message. - protected internal abstract Task SendAsync(ChatCommandGuidanceRequest request, CancellationToken cancellationToken = default); - - internal void AddHistory(ExtendedChatMessage item) - => history.Add(item); - - internal void NotifySending(ChatCommandGuidanceMessageEventArgs args) - => Sending?.Invoke(this, args); - - internal void NotifySendFailed(ChatCommandGuidanceErrorEventArgs args) - => SendFailed?.Invoke(this, args); - - internal void NotifyReceived(ChatCommandGuidanceMessageEventArgs args) - => Received?.Invoke(this, args); - - internal void NotifyProcessed(ChatCommandGuidanceMessageEventArgs args) - => Processed?.Invoke(this, args); - - internal void ProcessCommands(ChatCommandGuidanceTopic topic, ChatCommandGuidanceResponse response) - { - var details = response?.Details; - if (details == null || topic == null) return; - foreach (var item in details) - { - if (string.IsNullOrWhiteSpace(item.Key) || item.Value == null || !commands.TryGetValue(item.Key, out var command)) continue; - command.ClientProcess(this, new ChatCommandGuidanceClientProcessingArgs(item.Key, item.Value, topic, response)); - } - } -} - -/// -/// The event arguments for message of chat command guidance. -/// -public class ChatCommandGuidanceMessageEventArgs : EventArgs -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceMessageEventArgs class. - /// - /// The topic. - /// The response message. - /// The request message. - /// The response which the request is to reply. - public ChatCommandGuidanceMessageEventArgs(ChatCommandGuidanceTopic topic, ChatCommandGuidanceResponse response, ChatCommandGuidanceRequest request, ChatCommandGuidanceResponse reply) - { - Topic = topic; - Response = response; - Request = request; - Previous = reply; - } - - /// - /// Gets the chat topic. - /// - public ChatCommandGuidanceTopic Topic { get; } - - /// - /// Gets the request message. - /// - public ChatCommandGuidanceRequest Request { get; } - - /// - /// The response which the request is to reply. - /// - public ChatCommandGuidanceResponse Previous { get; } - - /// - /// Gets the response message. - /// - public ChatCommandGuidanceResponse Response { get; internal set; } -} - -/// -/// The client processing event arguments of chat command guidance. -/// -public class ChatCommandGuidanceClientProcessingArgs : EventArgs -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceClientProcessingArgs class. - /// - /// The command key. - /// The command value. - /// The chat topic. - /// The response message. - public ChatCommandGuidanceClientProcessingArgs(string command, JsonObjectNode value, ChatCommandGuidanceTopic topic, ChatCommandGuidanceResponse response) - { - Command = command; - Value = value; - Topic = topic; - Response = response; - } - - /// - /// Gets the command key. - /// - public string Command { get; } - - /// - /// Gets the command value. - /// - public JsonObjectNode Value { get; } - - /// - /// Gets the chat topic. - /// - public ChatCommandGuidanceTopic Topic { get; } - - /// - /// Gets the response message. - /// - public ChatCommandGuidanceResponse Response { get; } -} - -/// -/// The command guidance client for chat bot. -/// -public class LocalChatCommandGuidanceClient : BaseChatCommandGuidanceClient -{ - /// - /// Initializes a new instance of the LocalChatCommandGuidanceClient class. - /// - /// The user. - /// The engine. - public LocalChatCommandGuidanceClient(UserItemInfo user, BaseChatCommandGuidanceEngine engine) - : base(user) - { - Engine = engine ?? new StaticChatCommandGuidanceEngine(); - } - - /// - /// Gets the engine. - /// - protected BaseChatCommandGuidanceEngine Engine { get; } - - /// - protected internal override Task SendAsync(ChatCommandGuidanceRequest request, CancellationToken cancellationToken = default) - => Engine.ProcessAsync(request, cancellationToken); -} - -/// -/// The command guidance client for chat bot. -/// -public class OnlineChatCommandGuidanceClient : BaseChatCommandGuidanceClient -{ - /// - /// Initializes a new instance of the OnlineChatCommandGuidanceClient class. - /// - /// The user. - /// The URI of the web API. - /// The web client. - public OnlineChatCommandGuidanceClient(UserItemInfo user, Uri uri, OAuthClient client = null) - : base(user) - { - Uri = uri; - Client = client; - } - - /// - /// Gets the web client. - /// - public OAuthClient Client { get; } - - /// - /// Gets the URI of the web API. - /// - protected Uri Uri { get; } - - /// - protected internal override async Task SendAsync(ChatCommandGuidanceRequest request, CancellationToken cancellationToken = default) - { - if (Uri == null) return null; - var client = CreateHttpClient(); - var resp = await client.PostAsync(Uri, (JsonObjectNode)request, cancellationToken); - return (ChatCommandGuidanceResponse)resp; - } - - /// - /// Creates a JSON HTTP client. - /// - /// The type of response. - /// An optional callback raised on data received. - /// A new JSON HTTP client. - public virtual JsonHttpClient CreateHttpClient(Action> callback = null) - { - if (Client != null) return Client.Create(callback); - var client = new JsonHttpClient(); - if (callback != null) client.Received += (sender, ev) => - { - callback(ev); - }; - return client; - } -} diff --git a/Messages/Tasks/ChatCommandGuidanceContext.cs b/Messages/Tasks/ChatCommandGuidanceContext.cs deleted file mode 100644 index ba63c5c4..00000000 --- a/Messages/Tasks/ChatCommandGuidanceContext.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Trivial.Collection; -using Trivial.Data; -using Trivial.Users; -using Trivial.Text; - -namespace Trivial.Tasks; - -/// -/// The context of the command guidance for the chat bot. -/// -public class ChatCommandGuidanceContext -{ - private readonly JsonObjectNode infos; - private readonly Dictionary nextData = new(); - private readonly Dictionary nextInfo = new(); - private readonly ChatCommandGuidanceRequest request; - - /// - /// Initializes a new instance of the ChatCommandGuidanceContext class. - /// - /// The request. - /// The additional request information. - public ChatCommandGuidanceContext(ChatCommandGuidanceRequest request, object additionalRequestInfo = null) - { - NextInfo = new(); - nextInfo["_"] = NextInfo; - this.request = request ?? new(null, null, null, null); - UserMessageData = request.Data; - var history = request.History; - History = history == null ? new() : new(history); - Info = request.Info?.TryGetObjectValue("_"); - infos = request.Info; - ClientInfo = request.ClientInfo; - AdditionalRequestInfo = additionalRequestInfo; - } - - /// - /// Gets the instance identifier. - /// - public Guid Id { get; } = Guid.NewGuid(); - - /// - /// Gets the tracking identifier. - /// - public Guid TrackingId => request.TrackingId; - - /// - /// Gets the creation date time. - /// - public DateTime CreationTime { get; } = DateTime.Now; - - /// - /// Gets the replied date time. - /// - public DateTime? RepliedTime { get; private set; } - - /// - /// Gets the chat message from sender. - /// - public string UserMessage => request.Message; - - /// - /// Gets the chat message data from sender. - /// - public JsonObjectNode UserMessageData { get; } - - /// - /// Gets the original chat message result. - /// - public string OriginalAnswerMessage { get; private set; } - - /// - /// Gets the chat message result. - /// - public string AnswerMessage { get; private set; } - - /// - /// Gets the message kind. - /// - public string MessageKind { get; private set; } - - /// - /// Gets the user identifier. - /// - public string UserId => request.User?.Id; - - /// - /// Gets the user nickname. - /// - public string UserNickname => request.User?.Nickname; - - /// - /// Gets the user gender. - /// - public Genders Gender => request.User?.Gender ?? Genders.Unknown; - - /// - /// Gets the URI of the user avatar. - /// - public Uri UserAvatar => request.User?.AvatarUri; - - /// - /// Gets the history. - /// - public List History { get; } - - /// - /// Gets the context information. - /// The command guidance can access this to store useful data during this round. - /// - public JsonObjectNode Info { get; } - - /// - /// Gets next context information for context. - /// - public JsonObjectNode NextInfo { get; } - - /// - /// Gets the data of the business rich output. - /// - public JsonObjectNode AnswerData { get; } = new(); - - /// - /// Gets the client information. - /// - public JsonObjectNode ClientInfo { get; } - - /// - /// Gets the additional request information. - /// - public object AdditionalRequestInfo { get; } - - /// - /// Gets or set the tag object. - /// - public object Tag { get; set; } - - /// - /// Gets the prompt collection generated by each command guidance. - /// - internal SynchronizedList PromptCollection { get; } = new(); - - /// - /// Rewrites the answer message. - /// - /// The message. - public void RewriteAnswerMessage(string value) - => AnswerMessage = value; - - /// - /// Rewrites the answer message. - /// - /// The message. - /// true if append an empty line before original message. - public void AppendAnswerMessage(string value, bool additionalNewLine = false) - => AnswerMessage = string.IsNullOrWhiteSpace(OriginalAnswerMessage) ? value : string.Concat(AnswerMessage, Environment.NewLine, additionalNewLine ? Environment.NewLine : string.Empty, value); - - /// - /// Gets the response message. - /// - /// The response JSON object. - public ChatCommandGuidanceResponse GetResponse() - { - var resp = new ChatCommandGuidanceResponse(AnswerMessage, AnswerData, NextInfo, MessageKind, request, Id); - foreach (var item in nextData) - { - resp.Details[item.Key] = item.Value; - } - - return resp; - } - - /// - /// Creates the response modification instance. - /// - /// The new message text. - /// The additional data. - /// The modification kind. - /// The response modification instance. - public ChatCommandGuidanceResponseModification CreateResponseModification(string message, JsonObjectNode data = null, ChatMessageModificationKinds kind = ChatMessageModificationKinds.Modified) - => new(Id, message, data, kind); - - /// - /// Sets the answer message. - /// - /// The message text. - /// The message kind. - internal void SetAnswerMessage(string value, string kind) - { - RepliedTime = DateTime.Now; - AnswerMessage = value; - OriginalAnswerMessage = value; - MessageKind = kind; - } - - /// - /// Gets the information data. - /// - /// The command key. - /// The information data. - internal JsonObjectNode GetInfo(string key) - => key == null ? Info : infos?.TryGetObjectValue(key); - - /// - /// Gets the next information data of a specific command. - /// - /// The command key. - /// true if ensures; otherwise, false. - /// The next information data for context. - internal JsonObjectNode GetNextInfo(string command, bool init = false) - { - if (command == null) return NextInfo; - command = command.Trim().ToLowerInvariant(); - if (nextInfo.TryGetValue(command, out var result)) return result; - if (!init) return null; - result = new(); - nextInfo[command] = result; - return result; - } - - /// - /// Gets the answer data of a specific command. - /// - /// The command key. - /// true if ensures; otherwise, false. - /// The data for rich output. - internal JsonObjectNode GetAnswerData(string command, bool init = false) - { - if (command == null) return AnswerData; - command = command.Trim().ToLowerInvariant(); - if (nextData.TryGetValue(command, out var result)) return result; - if (!init) return null; - result = new(); - nextData[command] = result; - return result; - } - - /// - /// Adds prompt. - /// - /// The collection to add. - internal void AddPrompt(IEnumerable col) - { - if (col == null) return; - PromptCollection.AddRange(col); - } -} diff --git a/Messages/Tasks/ChatCommandGuidanceEngine.cs b/Messages/Tasks/ChatCommandGuidanceEngine.cs deleted file mode 100644 index 65eaacd3..00000000 --- a/Messages/Tasks/ChatCommandGuidanceEngine.cs +++ /dev/null @@ -1,593 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Trivial.Collection; -using Trivial.Data; -using Trivial.Users; -using Trivial.Text; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.ComponentModel.DataAnnotations; -using System.Security.Principal; -using Trivial.Security; -using Trivial.Net; - -namespace Trivial.Tasks; - -/// -/// The engine to generate smart prompt for chat bot. -/// -public abstract class BaseChatCommandGuidanceEngine -{ - private const string FuncPrefix = "⨍"; - private const string ParameterSeperator = "⫶"; - private readonly Dictionary commands = new(); - private readonly List monitors = new(); - - /// - /// Adds or removes the event on processing. - /// - public DataEventHandler Processing; - - /// - /// Adds or removes the event on process succeeded. - /// - public DataEventHandler Processed; - - /// - /// Adds or removes the event on process failed. - /// - public DataEventHandler ProcessFailed; - - /// - /// Adds or removes the event on source message is sending. - /// - public event DataEventHandler Sending; - - /// - /// Adds or removes the event on source message is failed to send or no response. - /// - public event EventHandler> SendFailed; - - /// - /// Adds or removes the event on source message is received. - /// - public event EventHandler Received; - - /// - /// Gets or sets a value indicating whether enable multiple threads per processing. - /// - public bool ParallelProcessing { get; set; } - - /// - /// Gets the additional information data. - /// - public JsonObjectNode Info { get; } = new(); - - /// - /// Gets the full name of the app service. - /// - public virtual string ServiceFullName { get; } - - /// - /// Gets the command collection. - /// - public ICollection Commands => commands.Values; - - /// - /// Gets the collection of all command key registered. - /// - public ICollection CommandKeys => commands.Keys; - - /// - /// Registers a command. - /// - /// The type of the command guidance. - public void Register() where T : BaseChatCommandGuidanceProvider - => Register(Activator.CreateInstance()); - - /// - /// Registers a command. - /// - /// The command key to use. - /// The type of the command guidance. - public void Register(string key) where T : BaseChatCommandGuidanceProvider - => Register(key, Activator.CreateInstance()); - - /// - /// Registers a command. - /// - /// The command. - public void Register(BaseChatCommandGuidanceProvider command) - => Register(command.Command, command); - - /// - /// Registers a command. - /// - /// The command key to use. - /// The command. - public void Register(string key, BaseChatCommandGuidanceProvider command) - { - key ??= command?.Command; - if (key == null) return; - key = key.Trim().ToLowerInvariant(); - if (string.IsNullOrEmpty(key) || key.Length < 2) return; - if (command == null) - { - commands.Remove(key); - return; - } - - commands[key] = command; - command.OnInit(this); - } - - /// - /// Tries to get the specific command. - /// - /// The command key. - /// The command guidance - public BaseChatCommandGuidanceProvider GetCommand(string key) - { - key = key?.Trim()?.ToLowerInvariant(); - if (string.IsNullOrEmpty(key)) return null; - return commands.TryGetValue(key, out var result) ? result : null; - } - - /// - /// Removes a specific command. - /// - /// The command key. - /// true if remove succeeded; otherwise, false. - public bool RemoveCommand(string key) - => commands.Remove(key); - - /// - /// Projects each element of a sequence into a new form by incorporating the element's key. - /// - /// The type of the value returned by selector. - /// A transform function to apply to each source element; the second parameter of the function represents the index of the source element. - /// A collection whose elements are the result of invoking the transform function on each element of source. - public IEnumerable SelectCommand(Func select) - { - if (select == null) yield break; - foreach (var kvp in commands) - { - if (kvp.Value == null) continue; - yield return select(kvp.Key, kvp.Value); - } - } - - /// - /// Projects each element of a sequence into a new form by incorporating the element's key. - /// - /// The type of the value returned by selector. - /// A filter by command guidance kind. - /// A transform function to apply to each source element; the second parameter of the function represents the index of the source element. - /// A collection whose elements are the result of invoking the transform function on each element of source. - public IEnumerable SelectCommand(ChatCommandGuidanceProviderKinds kind, Func select) - { - if (select == null) yield break; - foreach (var kvp in commands) - { - if (kvp.Value?.Kind != kind) continue; - yield return select(kvp.Key, kvp.Value); - } - } - - /// - /// Processes. - /// - /// The command guidance request. - /// The optional cancellation token. - /// The response. - public async Task ProcessAsync(ChatCommandGuidanceRequest request, CancellationToken cancellationToken = default) - { - if (request == null) return null; - var userDetails = request.User.GetProperty("_raw") ?? new JsonObjectNode(); - var reqInfo = GetAdditionalRequestInfo(request, userDetails); - var context = new ChatCommandGuidanceContext(request, reqInfo); - OnRequest(context); - var monitors = ChatCommandGuidanceEngineMonitorTask.Create(this.monitors, this, context); - var list = ChatCommandGuidanceTask.Create(this, commands, context); - var args = new DataEventArgs(context); - Processing?.Invoke(this, args); - monitors.OnRequest(this, userDetails); - context.AddPrompt(await list.GeneratePromptAsync(this, ParallelProcessing, cancellationToken)); - context.AddPrompt(await monitors.GeneratePromptAsync(this, cancellationToken)); - var prompt = GenerateDefaultPrompt(context); - prompt = ChatCommandGuidanceHelper.JoinWithEmptyLine(prompt, context.PromptCollection); - Sending?.Invoke(this, args); - ChatCommandGuidanceSourceResult answer; - try - { - answer = await SendAsync(context, prompt, cancellationToken); - answer ??= new(null, false, null); - } - catch (Exception ex) - { - OnSendError(context, ex); - monitors.OnSendError(this, ex); - SendFailed?.Invoke(this, new ChatCommandGuidanceErrorEventArgs(ex, context)); - throw; - } - - OnReceive(context, answer); - monitors.OnReceive(this, answer); - Received?.Invoke(this, new ChatCommandGuidanceSourceEventArgs(context, answer)); - context.SetAnswerMessage(answer.Message, answer.Kind); - if (answer.IsSuccessful) - { - var result = ChatCommandGuidanceHelper.ParseCommands(answer.Message, list, FuncPrefix).ToList(); - await result.PostProcessAsync(this, ParallelProcessing, cancellationToken); - OnCommandProccessed(result.Select(ele => ele.Args).ToList()); - Processed?.Invoke(this, args); - } - else - { - ProcessFailed?.Invoke(this, args); - } - - OnResponse(context); - monitors.OnResponse(this); - return context.GetResponse(); - } - - /// - /// Gets the additional request information. - /// - /// The request. - /// The user information in details. - /// The object. - protected virtual object GetAdditionalRequestInfo(ChatCommandGuidanceRequest request, JsonObjectNode userDetails) - => null; - - /// - /// Occurs on the request is received. - /// - /// The context - protected virtual void OnRequest(ChatCommandGuidanceContext context) - { - } - - /// - /// Occurs on the response is ready to return. - /// - /// The context - protected virtual void OnResponse(ChatCommandGuidanceContext context) - { - } - - /// - /// Occurs on the response is ready to return. - /// - /// The context - /// The answer - protected virtual void OnReceive(ChatCommandGuidanceContext context, ChatCommandGuidanceSourceResult answer) - { - } - - /// - /// Occurs on the response is ready to return. - /// - /// The context - /// The exception - protected virtual void OnSendError(ChatCommandGuidanceContext context, Exception ex) - { - } - - /// - /// Occurs on the command is processed. - /// - /// The arguments. - protected virtual void OnCommandProccessed(IList args) - { - } - - /// - /// Generates default prompt. - /// - /// The command guidance context. - /// The prompt. - protected virtual string GenerateDefaultPrompt(ChatCommandGuidanceContext context) - { - var sb = new StringBuilder(); - sb.Append("You are a helpful assistant. "); - var nickname = ChatCommandGuidanceHelper.FormatPromptName(context.UserNickname); - if (!string.IsNullOrWhiteSpace(nickname)) - { - sb.Append("User's name is "); - sb.Append(nickname); - sb.Append(context.Gender switch - { - Genders.Male => ". He", - Genders.Female => ". She", - _ => ". The user", - }); - } - else - { - sb.Append("The user"); - } - - sb.Append(" is using "); - sb.Append(ChatCommandGuidanceHelper.FormatPromptName(ServiceFullName) ?? "the service"); - var col = commands.Where(ChatCommandGuidanceHelper.IsSupportedCommand).ToList(); - if (col.Count < 1) - { - sb.Append('.'); - return sb.ToString(); - } - - sb.Append(" which also supports following commands if user asks for or is talking about. The commands are only available for you by adding a new line at the end of answer with character prefix in "); - sb.Append(FuncPrefix); - sb.Append(", command key, a seperator character and additional parameter. Each parameters are separeted by the seperator character. The seperator character is "); - sb.Append(ParameterSeperator); - sb.Append(". Don't let user send command directly."); - sb.AppendLine("|Command key|Command description|Parameter description|Command kind|"); - sb.AppendLine("|----------|------------------------|------------------------|------|"); - foreach (var kvp in col) - { - var command = kvp.Value; - sb.Append('|'); - sb.Append(command.Command); - sb.Append('|'); - sb.Append(command.Description); - sb.Append('|'); - sb.Append(command.ParameterDescription); - sb.Append('|'); - sb.Append(command.Kind); - sb.Append('|'); - } - - return sb.ToString(); - } - - /// - /// Sends the chat to get response. - /// - /// The command guidance context. - /// The prompt. - /// The optional cancellation token. - /// The answer result. - protected abstract Task SendAsync(ChatCommandGuidanceContext context, string prompt, CancellationToken cancellationToken = default); -} - -/// -/// The engine to generate smart prompt for chat bot. -/// -public class OnlineChatCommandGuidanceEngine : BaseChatCommandGuidanceEngine -{ - /// - /// Initializes a new instance of the OnlineChatCommandGuidanceEngine class. - /// - /// The URI of the web API. - /// The web client. - public OnlineChatCommandGuidanceEngine(Uri uri, OAuthClient client = null) - { - Uri = uri; - Client = client; - } - - /// - /// Gets the web client. - /// - public OAuthClient Client { get; } - - /// - /// Gets the URI of the web API. - /// - protected Uri Uri { get; } - - /// - protected override async Task SendAsync(ChatCommandGuidanceContext context, string prompt, CancellationToken cancellationToken = default) - { - if (Uri == null) return null; - var client = CreateHttpClient(); - var resp = await client.PostAsync(Uri, CreateSendBody(context, prompt), cancellationToken); - return GetAnswer(context, resp); - } - - /// - /// Creates the JSON object to send as HTTP body. - /// - /// The context. - /// The prompt. - /// The request body. - protected virtual JsonObjectNode CreateSendBody(ChatCommandGuidanceContext context, string prompt) - { - var messages = new JsonArrayNode(); - if (!string.IsNullOrEmpty(prompt)) messages.Add(new JsonObjectNode - { - { "role", "system" }, - { "content", prompt } - }); - foreach (var message in context.History) - { - var kind = message?.Category?.Trim()?.ToLowerInvariant(); - if (string.IsNullOrEmpty(kind)) continue; - var role = kind switch - { - "user" or "me" => "user", - "bot" or "ai" or "chatbot" or "assistant" => "assistant", - _ => null - }; - if (role == null) continue; - messages.Add(new JsonObjectNode - { - { "role", role }, - { "content", message.Message } - }); - } - - messages.Add(new JsonObjectNode - { - { "role", "user" }, - { "content", context.UserMessage } - }); - return new JsonObjectNode - { - { "messages", messages } - }; - } - - /// - /// Gets the source answer result. - /// - /// The context. - /// The original JSON object. - /// The source answer result. - protected virtual ChatCommandGuidanceSourceResult GetAnswer(ChatCommandGuidanceContext context, JsonObjectNode json) - { - if (json == null) return null; - context.Info.SetValue("response", json); - var data = json.TryGetArrayValue("choices")?.TryGetObjectValue(0)?.TryGetObjectValue("message"); - if (data?.TryGetStringTrimmedValue("role", true)?.ToLowerInvariant() != "assistant") - return new(json.TryGetStringTrimmedValue("message") ?? json.TryGetStringTrimmedValue("msg"), false, "error"); - return new(data.TryGetStringValue("content"), true, "bot"); - } - - /// - /// Creates a JSON HTTP client. - /// - /// The type of response. - /// An optional callback raised on data received. - /// A new JSON HTTP client. - public virtual JsonHttpClient CreateHttpClient(Action> callback = null) - { - if (Client != null) return Client.Create(callback); - var client = new JsonHttpClient(); - if (callback != null) client.Received += (sender, ev) => - { - callback(ev); - }; - return client; - } -} - -/// -/// The engine to generate smart prompt for chat bot. -/// -public class ProxyChatCommandGuidanceEngine : BaseChatCommandGuidanceEngine -{ - /// - /// Initializes a new instance of the ProxyChatCommandGuidanceEngine class. - /// - /// The chat topic. - public ProxyChatCommandGuidanceEngine(ChatCommandGuidanceTopic topic) - { - Topic = topic; - } - - /// - /// Initializes a new instance of the ProxyChatCommandGuidanceEngine class. - /// - /// The chat client. - public ProxyChatCommandGuidanceEngine(BaseChatCommandGuidanceClient client) - { - Topic = client?.NewTopic(); - } - - /// - /// Gets the web client. - /// - protected ChatCommandGuidanceTopic Topic { get; } - - /// - protected override async Task SendAsync(ChatCommandGuidanceContext context, string prompt, CancellationToken cancellationToken = default) - { - if (Topic == null) return null; - var resp = await Topic.SendAsync(context.UserMessage, context.UserMessageData, cancellationToken); - context.NextInfo.SetRange(resp.Info); - foreach (var details in resp.Details) - { - var json = context.GetAnswerData(details.Key, true); - json.SetRange(details.Value); - } - - return new(resp.Message, true, resp.Kind); - } -} - -/// -/// The test engine for chat bot. -/// -public class StaticChatCommandGuidanceEngine : BaseChatCommandGuidanceEngine -{ - private readonly List mock = new(); - - /// - /// Gets or sets the default answer result. - /// - public ChatCommandGuidanceSourceResult DefaultAnswer { get; set; } - - /// - /// Adds mock data. - /// - /// The question. - /// The answer. - public void AddAnswer(string question, ChatCommandGuidanceSourceResult answer) - => mock.Add(new(question, answer)); - - /// - /// Adds mock data. - /// - /// The question. - /// The answer. - public void AddAnswer(string question, string answer) - => mock.Add(new(question, new(answer, true, "mock"))); - - /// - /// Adds mock data. - /// - /// The questions. - /// The answer. - public void AddAnswer(IEnumerable questions, ChatCommandGuidanceSourceResult answer) - => mock.Add(new(questions, answer)); - - /// - /// Adds mock data. - /// - /// The questions. - /// The answer. - public void AddAnswer(IEnumerable questions, string answer) - => mock.Add(new(questions, new(answer, true, "mock"))); - - /// - protected override Task SendAsync(ChatCommandGuidanceContext context, string prompt, CancellationToken cancellationToken = default) - { - ChatCommandGuidanceSourceResult answer = null; - foreach (var item in mock) - { - if (!item.Questions.Contains(context.UserMessage, StringComparer.CurrentCultureIgnoreCase)) continue; - answer = item.Answer; - break; - } - - return Task.FromResult(answer ?? DefaultAnswer ?? new ChatCommandGuidanceSourceResult(null, false, "error")); - } -} - -internal class ChatCommandGuidanceQnaItem -{ - public ChatCommandGuidanceQnaItem(string question, ChatCommandGuidanceSourceResult answer) - { - Questions = new() - { - question - }; - Answer = answer; - } - - public ChatCommandGuidanceQnaItem(IEnumerable questions, ChatCommandGuidanceSourceResult answer) - { - Questions = questions.ToList(); - Answer = answer; - } - - public List Questions { get; } - - public ChatCommandGuidanceSourceResult Answer { get; } -} diff --git a/Messages/Tasks/ChatCommandGuidanceEngineMonitor.cs b/Messages/Tasks/ChatCommandGuidanceEngineMonitor.cs deleted file mode 100644 index 117cb795..00000000 --- a/Messages/Tasks/ChatCommandGuidanceEngineMonitor.cs +++ /dev/null @@ -1,222 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Trivial.Text; - -namespace Trivial.Tasks; - -/// -/// The monitor of chat command guidance engine. -/// -public interface IChatCommandGuidanceEngineMonitor -{ - /// - /// Creates an object used for following steps. - /// - /// The engine. - /// The context. - /// The object. - object InitializeObject(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context); - - /// - /// Occurs on request. - /// - /// The engine. - /// The context. - /// The user information in details. - /// The object. - void OnRequest(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, JsonObjectNode userDetails, object obj); - - /// - /// Generates the prompt. - /// - /// The engine. - /// The context. - /// The object. - /// The optional cancellation token. - /// The prompt segment to append. - Task GeneratePromptAsync(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, object obj, CancellationToken cancellationToken = default); - - /// - /// Occurs on sending error. - /// - /// The engine. - /// The context. - /// The exception - /// The object. - void OnSendError(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, Exception ex, object obj); - - /// - /// Occurs on answer received. - /// - /// The engine. - /// The context. - /// The answer. - /// The object. - void OnReceive(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, ChatCommandGuidanceSourceResult answer, object obj); - - /// - /// Occurs on response returning. - /// - /// The engine. - /// The context. - /// The object. - void OnResponse(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, object obj); -} - -/// -/// The monitor of chat command guidance engine. -/// -public abstract class BaseChatCommandGuidanceEngineMonitor : IChatCommandGuidanceEngineMonitor where T : class -{ - /// - /// Creates an object used for following steps. - /// - /// The engine. - /// The context. - /// The object. - protected virtual T InitializeObject(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context) - => null; - - /// - /// Creates an object used for following steps. - /// - /// The engine. - /// The context. - /// The object. - object IChatCommandGuidanceEngineMonitor.InitializeObject(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context) - => InitializeObject(engine, context); - - /// - /// Occurs on request. - /// - /// The engine. - /// The context. - /// The user information in details. - /// The object. - protected virtual void OnRequest(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, JsonObjectNode userDetails, T obj) - { - } - - /// - /// Occurs on request. - /// - /// The engine. - /// The context. - /// The user information in details. - /// The object. - void IChatCommandGuidanceEngineMonitor.OnRequest(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, JsonObjectNode userDetails, object obj) - => OnRequest(engine, context, userDetails, obj as T); - - /// - /// Generates the prompt. - /// - /// The engine. - /// The context. - /// The object. - /// The optional cancellation token. - /// The prompt segment to append. - protected virtual Task GeneratePromptAsync(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, T obj, CancellationToken cancellationToken = default) - => Task.FromResult(null); - - /// - /// Generates the prompt. - /// - /// The engine. - /// The context. - /// The object. - /// The optional cancellation token. - /// The prompt segment to append. - Task IChatCommandGuidanceEngineMonitor.GeneratePromptAsync(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, object obj, CancellationToken cancellationToken) - => GeneratePromptAsync(engine, context, obj as T, cancellationToken); - - /// - /// Occurs on sending error. - /// - /// The engine. - /// The context. - /// The exception - /// The object. - protected virtual void OnSendError(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, Exception ex, T obj) - { - } - - /// - /// Occurs on sending error. - /// - /// The engine. - /// The context. - /// The exception - /// The object. - void IChatCommandGuidanceEngineMonitor.OnSendError(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, Exception ex, object obj) - => OnSendError(engine, context, ex, obj as T); - - /// - /// Occurs on answer received. - /// - /// The engine. - /// The context. - /// The answer. - /// The object. - protected virtual void OnReceive(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, ChatCommandGuidanceSourceResult answer, T obj) - { - } - - /// - /// Occurs on answer received. - /// - /// The engine. - /// The context. - /// The answer. - /// The object. - void IChatCommandGuidanceEngineMonitor.OnReceive(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, ChatCommandGuidanceSourceResult answer, object obj) - => OnReceive(engine, context, answer, obj as T); - - /// - /// Occurs on response returning. - /// - /// The engine. - /// The context. - /// The object. - protected virtual void OnResponse(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, T obj) - { - } - - /// - /// Occurs on response returning. - /// - /// The engine. - /// The context. - /// The object. - void IChatCommandGuidanceEngineMonitor.OnResponse(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context, object obj) - => OnResponse(engine, context, obj as T); -} - -internal class ChatCommandGuidanceEngineMonitorTask -{ - private ChatCommandGuidanceEngineMonitorTask(IChatCommandGuidanceEngineMonitor monitor, BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context) - { - if (monitor == null) return; - Monitor = monitor; - Context = context; - Ref = monitor.InitializeObject(engine, context); - } - - public ChatCommandGuidanceContext Context { get; } - - public IChatCommandGuidanceEngineMonitor Monitor { get; } - - public object Ref { get; } - - public static IEnumerable Create(IEnumerable monitors, BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceContext context) - { - if (monitors == null) yield break; - foreach (var monitor in monitors) - { - yield return new ChatCommandGuidanceEngineMonitorTask(monitor, engine, context); - } - } -} diff --git a/Messages/Tasks/ChatCommandGuidanceHelper.cs b/Messages/Tasks/ChatCommandGuidanceHelper.cs deleted file mode 100644 index a8067615..00000000 --- a/Messages/Tasks/ChatCommandGuidanceHelper.cs +++ /dev/null @@ -1,249 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Trivial.Text; -using Trivial.Users; - -namespace Trivial.Tasks; - -/// -/// The helper of chat command guidance. -/// -internal static class ChatCommandGuidanceHelper -{ - /// - /// Tests if the command is valid. - /// - /// The key value pair. - /// true if it is supported; otherwise, false. - public static bool IsSupportedCommand(KeyValuePair kvp) - { - if (kvp.Value == null) return false; - return kvp.Value.Kind switch - { - ChatCommandGuidanceProviderKinds.Command or ChatCommandGuidanceProviderKinds.Assistance or ChatCommandGuidanceProviderKinds.Recorder or ChatCommandGuidanceProviderKinds.Others => true, - _ => false - }; - } - - public static async Task GeneratePromptAsync(this ChatCommandGuidanceTask instance, BaseChatCommandGuidanceEngine engine, CancellationToken cancellationToken = default) - { - var prompt = await instance.Command.GeneratePromptAsync(engine, instance.Args, cancellationToken); - prompt = prompt?.Trim(); - if (!string.IsNullOrEmpty(prompt)) instance.Args.Context.PromptCollection.Add(prompt); - return prompt; - } - - public static async Task GeneratePromptAsync(this IEnumerable col, BaseChatCommandGuidanceEngine engine, bool parallelProcessing, CancellationToken cancellationToken = default) - { - if (parallelProcessing) - { - var list = new List>(); - foreach (var item in col) - { - var task = item.GeneratePromptAsync(engine, cancellationToken); - if (task != null) list.Add(task); - } - - return await Task.WhenAll(list.ToArray()); - } - - var result = new List(); - foreach (var item in col) - { - result.Add(await item.GeneratePromptAsync(engine, cancellationToken)); - } - - return result.ToArray(); - } - - public static Task PostProcessAsync(this ChatCommandGuidanceTask instance, BaseChatCommandGuidanceEngine engine, CancellationToken cancellationToken = default) - => instance.Command.PostProcessAsync(engine, instance.Args, cancellationToken); - - public static async Task PostProcessAsync(this IEnumerable col, BaseChatCommandGuidanceEngine engine, bool parallelProcessing, CancellationToken cancellationToken = default) - { - if (parallelProcessing) - { - var list = new List(); - foreach (var item in col) - { - var task = item.PostProcessAsync(engine, cancellationToken); - if (task != null) list.Add(task); - } - - await Task.WhenAll(list.ToArray()); - return; - } - - foreach (var item in col) - { - await item.PostProcessAsync(engine, cancellationToken); - } - } - - public static IEnumerable ParseCommands(string answer, IEnumerable list, string funcPrefix) - { - if (answer == null || list == null) yield break; - var lines = answer.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty(); - foreach (var line in lines) - { - var s = line?.Trim() ?? string.Empty; - if (s.Length < 4 || !s.StartsWith(funcPrefix)) continue; - s = s.Substring(funcPrefix.Length).TrimStart(); - var v = s.Split(new[] { '|' }, StringSplitOptions.None); - if (v == null || v.Length < 1) continue; - var key = v[0]?.Trim()?.ToLowerInvariant(); - if (string.IsNullOrEmpty(key)) continue; - var parameters = v.Skip(1).ToList(); - foreach (var command in list) - { - if (command.Args.Command != key) continue; - command.Args.Parameters = parameters.AsReadOnly(); - yield return command; - } - } - } - - /// - /// Occurs on request. - /// - /// The collection. - /// The engine. - /// The user information in details. - public static void OnRequest(this IEnumerable col, BaseChatCommandGuidanceEngine engine, JsonObjectNode userDetails) - { - foreach (var item in col) - { - item.Monitor.OnRequest(engine, item.Context, userDetails, item.Ref); - } - } - - /// - /// Generates the prompt. - /// - /// The collection. - /// The engine. - /// The optional cancellation token. - /// The prompt segment to append. - public static async Task> GeneratePromptAsync(this IEnumerable col, BaseChatCommandGuidanceEngine engine, CancellationToken cancellationToken = default) - { - var list = new List(); - foreach (var item in col) - { - var s = await item.Monitor.GeneratePromptAsync(engine, item.Context, item.Ref, cancellationToken); - list.Add(s); - } - - return list; - } - - /// - /// Occurs on sending failure. - /// - /// The collection. - /// The engine. - /// The exception. - public static void OnSendError(this IEnumerable col, BaseChatCommandGuidanceEngine engine, Exception ex) - { - foreach (var item in col) - { - item.Monitor.OnSendError(engine, item.Context, ex, item.Ref); - } - } - - /// - /// Occurs on answer receive. - /// - /// The collection. - /// The engine. - /// The answer. - public static void OnReceive(this IEnumerable col, BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceSourceResult answer) - { - foreach (var item in col) - { - item.Monitor.OnReceive(engine, item.Context, answer, item.Ref); - } - } - - /// - /// Occurs on response returning. - /// - /// The collection. - /// The engine. - public static void OnResponse(this IEnumerable col, BaseChatCommandGuidanceEngine engine) - { - foreach (var item in col) - { - item.Monitor.OnResponse(engine, item.Context, item.Ref); - } - } - - public static JsonObjectNode ToJson(Dictionary col) - { - if (col == null) return null; - var json = new JsonObjectNode(); - foreach (var item in col) - { - json[item.Key] = item.Value; - } - - return json; - } - - public static IEnumerable DeserializeChatMessages(JsonArrayNode arr) - { - if (arr == null) yield break; - foreach (var item in arr) - { - if (item is not JsonObjectNode json) continue; - yield return json; - } - } - - public static JsonArrayNode Serizalize(IEnumerable value) - { - if (value == null) return null; - var col = new JsonArrayNode(); - foreach (var item in value) - { - col.Add((JsonObjectNode)item); - } - - return col; - } - - public static bool IsNotEmpty(string s) - => !string.IsNullOrWhiteSpace(s); - - /// - /// Generates prompt. - /// - /// The default prompt. - /// The prompt segment generated by each command guidance. - /// The prompt. - public static string JoinWithEmptyLine(string line, IEnumerable lines) - { - var sb = new StringBuilder(line); - sb.AppendLine(); - sb.AppendLine(); - lines ??= new List(); - foreach (var item in lines) - { - sb.AppendLine(item); - sb.AppendLine(); - } - - return sb.ToString(); - } - - public static string FormatPromptName(string s) - => string.IsNullOrWhiteSpace(s) ? null : s.Trim().Replace(".", string.Empty).Replace(",", string.Empty).Replace(";", string.Empty).Replace("!", string.Empty).Replace("?", string.Empty).Replace("|", string.Empty).Replace("\"", string.Empty).Replace("\'", string.Empty).Replace("\t", string.Empty).Replace("\r", string.Empty).Replace("\n", string.Empty).Replace("…", string.Empty).Replace("。", string.Empty).Replace(",", string.Empty).Replace("!", string.Empty).Replace("?", string.Empty); - - public static void RunEmpty() - { - } -} diff --git a/Messages/Tasks/ChatCommandGuidanceMessage.cs b/Messages/Tasks/ChatCommandGuidanceMessage.cs deleted file mode 100644 index bd0f4b17..00000000 --- a/Messages/Tasks/ChatCommandGuidanceMessage.cs +++ /dev/null @@ -1,470 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Trivial.Collection; -using Trivial.Data; -using Trivial.Users; -using Trivial.Text; - -namespace Trivial.Tasks; - -/// -/// The request of chat command guidance. -/// -public class ChatCommandGuidanceRequest -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceRequest class. - /// - /// The user instance. - /// The message text. - /// The message data. - /// The chat history. - /// The context data from client. - /// The latest response. - public ChatCommandGuidanceRequest(UserItemInfo user, string message, JsonObjectNode data, IEnumerable history, JsonObjectNode clientContextData = null, ChatCommandGuidanceResponse response = null) - { - User = user ?? new(); - Message = message; - Data = data ?? new(); - History = history ?? new List(); - ClientContextData = clientContextData ?? new(); - TrackingId = response?.TrackingId ?? Guid.NewGuid(); - Info = response?.Info ?? new(); - ClientInfo = new(); - } - - /// - /// Initializes a new instance of the ChatCommandGuidanceRequest class. - /// - /// The user instance. - /// The message text. - /// The message data. - /// The chat history. - /// The context data from client. - /// The tracking identifier. - /// The additional information from latest response. - internal ChatCommandGuidanceRequest(UserItemInfo user, string message, JsonObjectNode data, IEnumerable history, JsonObjectNode clientContextData, Guid trackingId, JsonObjectNode info) - { - User = user ?? new(); - Message = message; - Data = data ?? new(); - History = history ?? new List(); - ClientContextData = clientContextData ?? new(); - TrackingId = trackingId; - Info = info ?? new(); - ClientInfo = new(); - } - - /// - /// Gets the user instance. - /// - internal UserItemInfo User { get; } - - /// - /// Gets the message identifier. - /// - public Guid Id { get; } = Guid.NewGuid(); - - /// - /// Gets the nickname of the sender. - /// - public string UserNickname => User.Nickname; - - /// - /// Gets the sender identifier. - /// - public string UserId => User.Id; - - /// - /// Gets the gender of the sender. - /// - public Genders Gender => User.Gender; - - /// - /// Gets the URI of the sender avatar. - /// - public Uri UserAvatar => User.AvatarUri; - - /// - /// Gets the tracking identifier. - /// - public Guid TrackingId { get; private set; } - - /// - /// Gets the additional client information. - /// - public JsonObjectNode ClientInfo { get; private set; } - - /// - /// Gets the message. - /// - public string Message { get; } - - /// - /// Gets the data. - /// - public JsonObjectNode Data { get; } - - /// - /// Gets the information. - /// - public JsonObjectNode Info { get; private set; } - - /// - /// Gets the chat history. - /// - public IEnumerable History { get; } - - /// - /// Gets the context data from client. - /// - public JsonObjectNode ClientContextData { get; } - - /// - /// Deserialize to the request message of chat command guidance. - /// - /// The source value. - /// The resolver of user instance to override. - /// The request message instance. - public static ChatCommandGuidanceRequest Deserialize(JsonObjectNode value, Func user = null) - { - if (value is null) return null; - var u = value.TryGetObjectValue("sender"); - return new(user?.Invoke(u) ?? (UserItemInfo)u, value.TryGetStringValue("text") ?? value.TryGetStringValue("message"), value.TryGetObjectValue("data"), ChatCommandGuidanceHelper.DeserializeChatMessages(value.TryGetArrayValue("history")), value.TryGetObjectValue("ref")) - { - TrackingId = value.TryGetGuidValue("tracking") ?? Guid.NewGuid(), - Info = value.TryGetObjectValue("info") ?? new(), - ClientInfo = value.TryGetObjectValue("client") ?? new() - }; - } - - /// - /// Converts the JSON raw back. - /// - /// The source value. - /// The request instance. - public static implicit operator ChatCommandGuidanceRequest(JsonObjectNode value) - => Deserialize(value); - - /// - /// Converts to JSON object. - /// - /// The JSON value. - /// A JSON object. - public static explicit operator JsonObjectNode(ChatCommandGuidanceRequest value) - { - if (value is null) return null; - return new() - { - { "sender", (JsonObjectNode)value.User }, - { "tracking", value.TrackingId }, - { "text", value.Message }, - { "data", value.Data }, - { "info", value.Info}, - { "client", value.ClientInfo }, - { "history", ChatCommandGuidanceHelper.Serizalize(value.History) }, - { "ref", value.ClientContextData } - }; - } -} - -/// -/// The response of chat command guidance. -/// -public class ChatCommandGuidanceResponse -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceResponse class. - /// - /// The answer message text. - /// The answer data. - /// The information data for context. - /// The message kind. - /// The request message. - /// The identifier. - internal ChatCommandGuidanceResponse(string message, JsonObjectNode data, JsonObjectNode info = null, string kind = null, ChatCommandGuidanceRequest request = null, Guid? id = null) - { - Id = id ?? Guid.NewGuid(); - RequestId = request?.Id ?? Guid.Empty; - TrackingId = request?.TrackingId ?? Guid.NewGuid(); - Message = message; - Data = data; - Info = info; - Kind = kind; - ClientContextData = request?.ClientContextData; - } - - /// - /// Gets the message identifier. - /// - public Guid Id { get; } - - /// - /// Gets the identifier of request message. - /// - public Guid RequestId { get; private set; } - - /// - /// Gets the tracking identifier. - /// - public Guid TrackingId { get; private set; } - - /// - /// Gets the message. - /// - public string Message { get; } - - /// - /// Gets the response data. - /// - public JsonObjectNode Data { get; } - - /// - /// Gets the details. - /// - public Dictionary Details { get; } = new(); - - /// - /// Gets the information. - /// - public JsonObjectNode Info { get; } - - /// - /// Gets the message kind. - /// - public string Kind { get; } - - /// - /// Gets the context data from client. - /// - public JsonObjectNode ClientContextData { get; private set; } - - /// - /// Converts the JSON raw back. - /// - /// The source value. - /// The response instance. - public static implicit operator ChatCommandGuidanceResponse(JsonObjectNode value) - { - if (value is null) return null; - var result = new ChatCommandGuidanceResponse( - value.TryGetStringValue("text") ?? value.TryGetStringValue("message"), - value.TryGetObjectValue("data"), - value.TryGetObjectValue("info"), - value.TryGetStringTrimmedValue("kind", true), - null, - value.TryGetGuidValue("id") ?? Guid.NewGuid()) - { - RequestId = value.TryGetGuidValue("request") ?? Guid.Empty, - ClientContextData = value.TryGetObjectValue("ref") - }; - var detailsArr = value.TryGetObjectValue("details") ?? new(); - foreach (var item in detailsArr) - { - if (item.Value is not JsonObjectNode json) continue; - result.Details[item.Key] = json; - } - - return result; - } - - /// - /// Converts to JSON object. - /// - /// The JSON value. - /// A JSON object. - public static explicit operator JsonObjectNode(ChatCommandGuidanceResponse value) - { - if (value is null) return null; - return new() - { - { "id", value.Id }, - { "tracking", value.TrackingId }, - { "text", value.Message }, - { "data", value.Data }, - { "details", ChatCommandGuidanceHelper.ToJson(value.Details) }, - { "info", value.Info}, - { "ref", value.ClientContextData } - }; - } -} - -/// -/// The response modification of chat command guidance. -/// -public class ChatCommandGuidanceResponseModification -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceResponseModification class. - /// - /// The response message. - /// The message text - public ChatCommandGuidanceResponseModification(ChatCommandGuidanceResponse response, string message) - { - Id = response?.Id ?? Guid.NewGuid(); - Data = response?.Data ?? new(); - Message = message; - ModificationKind = message == null ? ChatMessageModificationKinds.Removed : ChatMessageModificationKinds.Modified; - } - - /// - /// Initializes a new instance of the ChatCommandGuidanceResponseModification class. - /// - /// The identifier of the response message. - /// The message text - /// The message data. - /// The modification kind. - public ChatCommandGuidanceResponseModification(Guid id, string message, JsonObjectNode data = null, ChatMessageModificationKinds kind = ChatMessageModificationKinds.Modified) - { - Id = id; - Data = data ?? new(); - Message = message; - ModificationKind = kind; - } - - /// - /// Gets the message identifier. - /// - public Guid Id { get; } - - /// - /// Gets the creation data and time. - /// - public DateTime CreationTime { get; } - - /// - /// Gets the message text. - /// - public string Message { get; } - - /// - /// Gets the response data. - /// - public JsonObjectNode Data { get; } - - /// - /// Gets the modification kind. - /// - public ChatMessageModificationKinds ModificationKind { get; } - - /// - /// Converts to JSON object. - /// - /// The JSON value. - /// A JSON object. - public static explicit operator JsonObjectNode(ChatCommandGuidanceResponseModification value) - { - if (value is null) return null; - return new() - { - { "id", value.Id }, - { "text", value.Message }, - { "data", value.Data }, - { "created", value.CreationTime }, - { "modify", value.ModificationKind.ToString() } - }; - } -} - -/// -/// The result of chat command guidance. -/// -public class ChatCommandGuidanceSourceResult -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceSourceResult class. - /// - /// The message text. - /// A flag indicating whether the result is successful. - /// The message kind. - public ChatCommandGuidanceSourceResult(string message, bool success, string kind) - { - Message = message; - IsSuccessful = success; - Kind = ChatCommandGuidanceHelper.FormatPromptName(kind); - } - - /// - /// Gets the message text. - /// - public string Message { get; } - - /// - /// Gets a value indicating whether the result is successful. - /// - public bool IsSuccessful { get; } - - /// - /// Gets the message kind. - /// - public string Kind { get; } -} - -/// -/// The event arguments of command guidance source request. -/// -public class ChatCommandGuidanceSourceEventArgs : EventArgs -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceSourceEventArgs class. - /// - /// The context. - /// The result. - public ChatCommandGuidanceSourceEventArgs(ChatCommandGuidanceContext context, ChatCommandGuidanceSourceResult result) - { - Context = context; - Message = result?.Message; - IsSuccessful = result?.IsSuccessful ?? false; - Kind = result.Kind; - } - - /// - /// Gets the context. - /// - public ChatCommandGuidanceContext Context { get; } - - /// - /// Gets the message text. - /// - public string Message { get; } - - /// - /// Gets a value indicating whether the result is successful. - /// - public bool IsSuccessful { get; } - - /// - /// Gets the message kind. - /// - public string Kind { get; } -} - -/// -/// The error event arguments. -/// -public class ChatCommandGuidanceErrorEventArgs -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceErrorEventArgs class. - /// - /// The exception. - /// The context - public ChatCommandGuidanceErrorEventArgs(Exception ex, T context) - { - Exception = ex; - Context = context; - } - - /// - /// Gets the exception. - /// - public Exception Exception { get; } - - /// - /// Gets the context. - /// - public T Context { get; } -} diff --git a/Messages/Tasks/ChatCommandGuidanceProvider.cs b/Messages/Tasks/ChatCommandGuidanceProvider.cs deleted file mode 100644 index d201178b..00000000 --- a/Messages/Tasks/ChatCommandGuidanceProvider.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Security; -using System.Text; -using System.Threading.Tasks; -using Trivial.Collection; -using Trivial.Data; -using Trivial.Users; -using Trivial.Text; -using System.Threading; - -namespace Trivial.Tasks; - -/// -/// The types of command guidance for chat bot. -/// -public enum ChatCommandGuidanceProviderKinds : byte -{ - /// - /// Invalid. - /// - None = 0, - - /// - /// The command. - /// - Command = 1, - - /// - /// The assistance. - /// - Assistance = 2, - - /// - /// The recording operator. - /// - Recorder = 7, - - /// - /// The others. - /// - Others = 15 -} - -/// -/// The arguments of the command guidance for the chat bot. -/// -public class ChatCommandGuidanceArgs : EventArgs -{ - /// - /// Initializes a new instance of the ChatCommandGuidanceArgs class. - /// - /// The context. - /// The command key. - public ChatCommandGuidanceArgs(ChatCommandGuidanceContext context, string command) - { - Context = context; - Command = command; - AnswerData = context?.GetAnswerData(command) ?? new(); - Info = context?.GetInfo(command) ?? new(); - NextInfo = context?.GetNextInfo(command) ?? new(); - } - - /// - /// Gets the additional object. - /// - public object AdditionalObject { get; internal set; } - - /// - /// Gets the context. - /// - public ChatCommandGuidanceContext Context { get; } - - /// - /// Gets the context information. - /// The command guidance can access this to store useful data during this round. - /// - public JsonObjectNode Info { get; } - - /// - /// Gets the shared context information. - /// The command guidance can access this to store useful data during this round. - /// - public JsonObjectNode SharedInfo => Context?.Info; - - /// - /// Gets the command key. - /// - public string Command { get; } - - /// - /// Gets the parameters. - /// - public IList Parameters { get; internal set; } - - /// - /// Gets the data of the business rich output. - /// - public JsonObjectNode AnswerData { get; } - - /// - /// Gets the shared data of the business rich output. - /// - public JsonObjectNode SharedAnswerData => Context?.AnswerData; - - /// - /// Gets the next context information so that the command guidance can access. - /// - public JsonObjectNode NextInfo { get; } = new(); - - /// - /// Gets the shared next context information. - /// - public JsonObjectNode SharedNextInfo => Context?.NextInfo; - - /// - /// Gets the original chat message result. - /// - public string OriginalAnswerMessage => Context?.OriginalAnswerMessage; - - /// - /// Gets the chat message result. - /// - public string AnswerMessage => Context?.AnswerMessage; - - /// - /// Gets the creation date and time. - /// - public DateTime CreationTime { get; } = DateTime.Now; -} - -/// -/// The command guidance provider for chat bot. -/// -public abstract class BaseChatCommandGuidanceProvider -{ - /// - /// Initializes a new instance of the BaseChatCommandGuidance class. - /// - /// The command key. - /// The command description. - /// The command parameter description. - /// The command type. - protected BaseChatCommandGuidanceProvider(string command, string description, string parameterDescription, ChatCommandGuidanceProviderKinds type) - { - Command = command; - Description = description; - ParameterDescription = parameterDescription; - Kind = type; - } - - /// - /// Gets the command key. - /// - public virtual string Command { get; } - - /// - /// Gets the command description. - /// - public virtual string Description { get; } - - /// - /// Gets the command parameter description. - /// - public virtual string ParameterDescription { get; } - - /// - /// Gets the kind of the command. - /// - public virtual ChatCommandGuidanceProviderKinds Kind { get; } - - /// - /// Occurs on initialized. - /// - protected internal virtual void OnInit(BaseChatCommandGuidanceEngine engine) - { - } - - /// - /// Occurs on initialized. - /// - /// The client. - protected internal virtual void OnInit(BaseChatCommandGuidanceClient client) - { - } - - /// - /// Creates an object used for following steps. - /// - /// The engine. - /// The arguments. - /// The object. - protected internal virtual object InitializeObject(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceArgs args) - => null; - - /// - /// Tests if the command guidance is available. - /// - /// The engine. - /// The arguments. - /// true if the command guidance is available; otherwise, false. - protected internal virtual bool IsAvailable(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceArgs args) - => args != null; - - /// - /// Generates the prompt. - /// - /// The engine. - /// The arguments. - /// The optional cancellation token. - /// The prompt segment to append. - protected internal virtual Task GeneratePromptAsync(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceArgs args, CancellationToken cancellationToken = default) - => Task.FromResult(null); - - /// - /// Post processes. - /// - /// The engine. - /// The arguments. - /// The optional cancellation token. - /// A Task that represents the work queued to execute in the ThreadPool. - protected internal abstract Task PostProcessAsync(BaseChatCommandGuidanceEngine engine, ChatCommandGuidanceArgs args, CancellationToken cancellationToken = default); - - /// - /// Post processes on client side. - /// - /// The client. - /// The arguments. - protected internal virtual void ClientProcess(BaseChatCommandGuidanceClient client, ChatCommandGuidanceClientProcessingArgs args) - { - } - - /// - /// Runs empty logic. - /// - /// The optional cancellation token. - /// A Task that represents the work queued to execute in the ThreadPool. - protected static Task RunEmptyAsync(CancellationToken cancellationToken = default) - => Task.Run(ChatCommandGuidanceHelper.RunEmpty, cancellationToken); -} - -internal class ChatCommandGuidanceTask -{ - private ChatCommandGuidanceTask(BaseChatCommandGuidanceEngine engine, string key, BaseChatCommandGuidanceProvider command, ChatCommandGuidanceContext context) - { - Command = command; - Args = new ChatCommandGuidanceArgs(context, key); - Args.AdditionalObject = command.InitializeObject(engine, Args); - IsAvailable = command.IsAvailable(engine, Args); - } - - public BaseChatCommandGuidanceProvider Command { get; } - - public ChatCommandGuidanceArgs Args { get; } - - public bool IsAvailable { get; } - - public static IEnumerable Create(BaseChatCommandGuidanceEngine engine, IDictionary collection, ChatCommandGuidanceContext context) - { - if (collection == null || context == null) yield break; - foreach (var kvp in collection) - { - var key = kvp.Key; - var command = kvp.Value; - if (string.IsNullOrEmpty(key) || command == null) continue; - var task = new ChatCommandGuidanceTask(engine, key, command, context); - if (task.IsAvailable) yield return task; - } - } -} diff --git a/Messages/Tasks/ChatCommandGuidanceTopic.cs b/Messages/Tasks/ChatCommandGuidanceTopic.cs deleted file mode 100644 index e8ddfca0..00000000 --- a/Messages/Tasks/ChatCommandGuidanceTopic.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Nodes; -using System.Threading.Tasks; -using Trivial.Collection; -using Trivial.Data; -using Trivial.Users; -using Trivial.Text; -using System.Threading; -using System.Net.NetworkInformation; - -namespace Trivial.Tasks; - -/// -/// The chat topic. -/// -public class ChatCommandGuidanceTopic -{ - private readonly BaseChatCommandGuidanceClient client; - internal readonly List history = new(); - - /// - /// Initializes a new instance of the ChatCommandGuidanceTopic class. - /// - /// The client. - internal ChatCommandGuidanceTopic(BaseChatCommandGuidanceClient client) - { - this.client = client; - History = history.AsReadOnly(); - } - - /// - /// Adds or removes the event on message is sending. - /// - public event EventHandler Sending; - - /// - /// Adds or removes the event on message is failed to send or no response. - /// - public event EventHandler> SendFailed; - - /// - /// Adds or removes the event on message is received. - /// - public event EventHandler Received; - - /// - /// Adds or removes the event on message is post processed. - /// - public event EventHandler Processed; - - /// - /// Gets the additional information data. - /// - public JsonObjectNode Info { get; } = new(); - - /// - /// Gets the shared information data. - /// - public JsonObjectNode SharedInfo => client.Info; - - /// - /// Gets the current user info. - /// - public UserItemInfo User => client.User; - - /// - /// Gets the chat history. - /// - public IList History { get; } - - /// - /// Gets the latest request. - /// - public ChatCommandGuidanceRequest LatestRequest { get; private set; } - - /// - /// Gets the latest response. - /// - public ChatCommandGuidanceResponse LatestResponse { get; private set; } - - /// - /// Creates a new topic. - /// - /// The topic instance. - public ChatCommandGuidanceTopic NewTopic() - => client.NewTopic(); - - /// - /// Sends a request to get response. - /// - /// The message text. - /// The message data. - /// The optional cancellation token. - /// The response instance. - public async Task SendAsync(string message, JsonObjectNode data, CancellationToken cancellationToken = default) - { - var result = await SendAsync(message, data, LatestResponse, cancellationToken); - return result?.Response; - } - - /// - /// Sends a request to get response. - /// - /// The message text. - /// The message data. - /// The callback. - /// The optional cancellation token. - /// The reply instance with response. - public async Task SendAsync(string message, JsonObjectNode data, Action callback, CancellationToken cancellationToken = default) - { - var result = await SendAsync(message, data, LatestResponse, cancellationToken); - callback?.Invoke(result); - return result?.Response; - } - - /// - /// Sends a request to get response. - /// - /// The message text. - /// The message data. - /// The response message to reply; or null, if starts a new topic. - /// The optional cancellation token. - /// The response instance. - internal async Task SendAsync(string message, JsonObjectNode data, ChatCommandGuidanceResponse reply, CancellationToken cancellationToken = default) - { - var user = client.User; - var request = new ChatCommandGuidanceRequest(user, message, data, new List(history), null, reply); - var args = new ChatCommandGuidanceMessageEventArgs(this, null, request, reply); - client.OnRequestCreate(args); - Sending?.Invoke(this, args); - client.NotifySending(args); - LatestRequest = request; - var record = new ExtendedChatMessage(request.Id, user, message, DateTime.Now, data) - { - Category = client.RequestMessageKind - }; - history.Add(record); - client.AddHistory(record); - client.OnSend(args); - ChatCommandGuidanceResponse response; - try - { - response = await client.SendAsync(request, cancellationToken); - } - catch (Exception ex) - { - var exArgs = new ChatCommandGuidanceErrorEventArgs(ex, args); - SendFailed?.Invoke(this, exArgs); - client.NotifySendFailed(exArgs); - throw; - } - - args.Response = response; - Received?.Invoke(this, args); - client.NotifyReceived(args); - LatestResponse = response; - record = new ExtendedChatMessage(response.Id, user, response.Message, DateTime.Now, response.Data) - { - Category = response.Kind - }; - history.Add(record); - client.AddHistory(record); - client.OnReceive(args); - client.ProcessCommands(this, response); - client.OnProcessed(args); - Processed?.Invoke(this, args); - client.NotifyProcessed(args); - return new(this, response); - } -} - -/// -/// The reply information for chat command guidance topic. -/// -public class ChatCommandGuidanceReply -{ - private readonly ChatCommandGuidanceTopic topic; - - /// - /// Initializes a new instance of the ChatCommandGuidanceReply class. - /// - /// - /// - internal ChatCommandGuidanceReply(ChatCommandGuidanceTopic topic, ChatCommandGuidanceResponse response) - { - this.topic = topic; - Response = response; - } - - /// - /// Gets the response. - /// - public ChatCommandGuidanceResponse Response { get; } - - /// - /// Gets a value indicating whether the response has replied. - /// - public bool HasReplied { get; private set; } - - /// - /// Gets the creation date and time. - /// - public DateTime CreationTime { get; } = DateTime.Now; - - /// - /// Creates a request. - /// - /// The message text. - /// The message data. - /// The optional cancellation token. - /// The request instance. - public async Task SendAsync(string message, JsonObjectNode data, CancellationToken cancellationToken = default) - { - HasReplied = true; - var result = await topic.SendAsync(message, data, Response, cancellationToken); - return result.Response; - } - - /// - /// Creates a request. - /// - /// The message text. - /// The message data. - /// The callback. - /// The optional cancellation token. - /// The request instance. - public async Task SendAsync(string message, JsonObjectNode data, Action callback, CancellationToken cancellationToken = default) - { - HasReplied = true; - var result = await topic.SendAsync(message, data, Response, cancellationToken); - callback?.Invoke(result); - return result.Response; - } -} diff --git a/Messages/Text/ExtendedChatClient.cs b/Messages/Text/ExtendedChatClient.cs deleted file mode 100644 index 4a01cddc..00000000 --- a/Messages/Text/ExtendedChatClient.cs +++ /dev/null @@ -1,330 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Trivial.Data; -using Trivial.Users; - -namespace Trivial.Text; - -/// -/// The states of send result for chat message. -/// -public enum ExtendedChatMessageSendResultStates : byte -{ - /// - /// Not send. - /// - NotSend = 0, - - /// - /// Success. - /// - Success = 1, - - /// - /// Network issue or timeout. - /// - NetworkIssue = 2, - - /// - /// Unauthorized or fobidden to send. - /// - Forbidden = 3, - - /// - /// Traffic limitation for connection. - /// - Throttle = 4, - - /// - /// Format error or invalid request. - /// - RequestError = 5, - - /// - /// Client error. - /// - ClientError = 6, - - /// - /// Unknown server-side error. - /// - ServerError = 7, - - /// - /// Other error. - /// - OtherError = 10 -} - -/// -/// The provider for chat client. -/// -public interface IExtendedChatClientProvider -{ - /// - /// Adds or removes the event occurs on message received. - /// - event DataEventHandler MessageReceived; - - /// - /// Gets the current user. - /// - UserItemInfo User { get; } - - /// - /// Sends the message. - /// - /// The thread to send message. - /// The message to send. - /// The optional cancellation token. - /// The result state and other information. - Task SendAsync(IExtendedChatThread to, ExtendedChatMessage message, CancellationToken cancellationToken = default); - - /// - /// Tries to get the user. - /// - /// The user identifier. - /// The optional cancellation token. - /// The user information instance; or null, if not exists. - Task TryGetUserAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Gets latest threads. - /// - /// The optional cancellation token. - /// The thread list. - Task> ListThreadAsync(CancellationToken cancellationToken = default); - - /// - /// Searches threads. - /// - /// The search query. - /// The optional cancellation token. - /// The thread list. - Task> ListThreadAsync(string q, CancellationToken cancellationToken = default); -} - -/// -/// The chat client. -/// -public class ExtendedChatClient -{ - /// - /// Initializes a new instance of the ExtendedChatClient class. - /// - /// The chat client provider. - public ExtendedChatClient(IExtendedChatClientProvider provider) - { - Provider = provider; - } - - /// - /// Gets or sets the user info. - /// - public UserItemInfo User => Provider.User; - - /// - /// Gets the i. - /// - protected IExtendedChatClientProvider Provider { get; } - - /// - /// Sends the message. - /// - /// The thread to send message. - /// The message text to send. - /// The additional data to send. - /// The optional cancellation token. - /// The message request. - public ExtendedChatMessageRequest Send(IExtendedChatThread to, string message, JsonObjectNode info, CancellationToken cancellationToken = default) - { - var req = new ExtendedChatMessageRequest(Provider, to, new ExtendedChatMessage(User, message, null, info)); - _ = req.GetResultAsync(cancellationToken); - return req; - } - - /// - /// Tries to get the user. - /// - /// The user identifier. - /// The optional cancellation token. - /// The user information instance; or null, if not exists. - public Task TryGetUserAsync(string id, CancellationToken cancellationToken = default) - => Provider?.TryGetUserAsync(id, cancellationToken) ?? Task.FromResult(null); - - /// - /// Gets latest threads. - /// - /// The optional cancellation token. - /// The thread list. - public async Task> ListThreadAsync(CancellationToken cancellationToken = default) - { - if (Provider == null) return new(); - return await Provider.ListThreadAsync(cancellationToken) ?? new(); - } - - /// - /// Searches threads. - /// - /// The search query. - /// The optional cancellation token. - /// The thread list. - public async Task> ListThreadAsync(string q, CancellationToken cancellationToken = default) - { - if (Provider == null) return new(); - return await Provider.ListThreadAsync(q, cancellationToken) ?? new(); - } -} - -/// -/// The sending result of chat message. -/// -public class ExtendedChatMessageSendResult -{ - /// - /// Initializes a new instance of the ExtendedChatMessageSendResult class. - /// - /// The result state. - public ExtendedChatMessageSendResult(ExtendedChatMessageSendResultStates state) - { - State = state; - Info = new(); - } - - /// - /// Initializes a new instance of the ExtendedChatMessageSendResult class. - /// - /// The result state. - /// - /// - public ExtendedChatMessageSendResult(ExtendedChatMessageSendResultStates state, string message, JsonObjectNode info = null) - { - State = state; - Message = message; - Info = info ?? new(); - } - - /// - /// Gets the result state. - /// - public ExtendedChatMessageSendResultStates State { get; } - - /// - /// Gets the additional response message, such like error message or result notes. - /// - public string Message { get; } - - /// - /// Gets the additional information. - /// - public JsonObjectNode Info { get; } - - /// - /// Gets a value indicating whether the state is a retry-able one. - /// - internal bool CanRetry => State == ExtendedChatMessageSendResultStates.NotSend || State == ExtendedChatMessageSendResultStates.Throttle || State == ExtendedChatMessageSendResultStates.NetworkIssue; -} - -/// -/// The chat message request. -/// -public class ExtendedChatMessageRequest -{ - private Task task; - private readonly IExtendedChatClientProvider client; - - /// - /// Initializies a new instance of the ExtendedChatMessageRequest class. - /// - /// The client provider. - /// The thread to send message. - /// The message to send. - public ExtendedChatMessageRequest(IExtendedChatClientProvider client, IExtendedChatThread to, ExtendedChatMessage message) - { - this.client = client; - Thread = to; - Message = message; - } - - /// - /// Gets the message sent. - /// - public ExtendedChatMessage Message { get; } - - /// - /// Gets the thread to send message. - /// - public IExtendedChatThread Thread { get; } - - /// - /// Gets the result. - /// - public ExtendedChatMessageSendResult Result { get; private set; } = new(ExtendedChatMessageSendResultStates.NotSend); - - /// - /// Gets a value indicating whether the message is sending. - /// - public bool IsSending => task != null; - - /// - /// Gets the result. - /// - /// The result state and other information. - public Task GetResultAsync() - => GetResultAsync(false); - - /// - /// Gets the result. - /// - /// true if resend for networking issue; otherwise, false. - /// The result state and other information. - public async Task GetResultAsync(bool retry) - { - var t = task; - if (t != null) - { - try - { - Result = await t; - } - finally - { - task = null; - Result ??= new(ExtendedChatMessageSendResultStates.OtherError); - } - - return Result; - } - - if (!retry || Result == null || !Result.CanRetry) return Result ?? new(ExtendedChatMessageSendResultStates.OtherError); - await GetResultAsync(default(CancellationToken)); - return Result; - } - - /// - /// Gets the result. - /// - /// The optional cancellation token. - /// The result state and other information. - internal async Task GetResultAsync(CancellationToken cancellationToken) - { - if (client == null || Thread == null) return Result = new(ExtendedChatMessageSendResultStates.ClientError); - task = client.SendAsync(Thread, Message, cancellationToken); - try - { - Result = await task; - } - finally - { - Result ??= new(ExtendedChatMessageSendResultStates.OtherError); - task = null; - } - - return Result; - } -} diff --git a/Messages/Text/ExtendedChatMessage.cs b/Messages/Text/ExtendedChatMessage.cs deleted file mode 100644 index 3f9509bd..00000000 --- a/Messages/Text/ExtendedChatMessage.cs +++ /dev/null @@ -1,951 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using Trivial.Collection; -using Trivial.Data; -using Trivial.Reflection; -using Trivial.Tasks; -using Trivial.Users; - -namespace Trivial.Text; - -/// -/// The modification kinds of the message. -/// -public enum ChatMessageModificationKinds : byte -{ - /// - /// The message is neve modified. - /// - Original = 0, - - /// - /// The streaming message which means the message is transferring by continious updating. - /// - Streaming = 1, - - /// - /// The message has been modified by sender. - /// - Modified = 2, - - /// - /// The message has been modified and is open to update by others. - /// - Collaborative = 3, - - /// - /// The message has been removed by sender. - /// - Removed = 5, - - /// - /// The message is banned by system. - /// - Ban = 6, - - /// - /// Others. - /// - Others = 15 -} - -/// -/// The chat message record. -/// -public class ExtendedChatMessage : BaseResourceEntityInfo -{ - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The nickname of the sender. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - public ExtendedChatMessage(UserItemInfo sender, string message, DateTime? creation = null, JsonObjectNode info = null) - : this(Guid.NewGuid(), sender, message, creation, info, null) - { - } - - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The message identifier. - /// The nickname of the sender. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The message type. - public ExtendedChatMessage(Guid id, UserItemInfo sender, string message, DateTime? creation = null, JsonObjectNode info = null, string type = null) - : this(ExtendedChatMessages.ToIdString(id), sender, message, creation, info, type) - { - } - - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The message identifier. - /// The nickname of the sender. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The message type. - public ExtendedChatMessage(string id, UserItemInfo sender, string message, DateTime? creation = null, JsonObjectNode info = null, string type = null) - : base(id) - { - var time = creation ?? DateTime.Now; - SetProperty(nameof(Sender), sender); - SetProperty(nameof(Message), message); - if (!string.IsNullOrEmpty(type)) SetProperty(nameof(MessageType), type); - SetProperty(nameof(CreationTime), time); - SetProperty(nameof(LastModificationTime), time); - Info = info ?? new(); - } - - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The JSON object to parse. - public ExtendedChatMessage(JsonObjectNode json) - { - if (json is null) return; - Id = json.TryGetStringTrimmedValue("id", true) ?? json.Id; - SetProperty(nameof(Sender), (UserItemInfo)json.TryGetObjectValue("sender")); - SetProperty(nameof(Message), json.TryGetStringValue("text") ?? json.TryGetStringValue("message")); - SetProperty(nameof(MessageType), json.TryGetStringTrimmedValue("type", true)); - SetProperty(nameof(CreationTime), json.TryGetDateTimeValue("created") ?? DateTime.Now); - SetProperty(nameof(LastModificationTime), json.TryGetDateTimeValue("modified") ?? DateTime.Now); - Info = json.TryGetObjectValue("info") ?? new(); - Category = json.TryGetStringTrimmedValue("category", true); - SetProperty("Data", json.TryGetObjectValue("data")); - } - - /// - /// Gets the sender. - /// - public UserItemInfo Sender => GetCurrentProperty(); - - /// - /// Gets the plain text of the message. - /// - public string Message - { - get - { - return GetCurrentProperty(); - } - - set - { - if (!SetCurrentProperty(value)) return; - if (GetProperty(nameof(ModificationKind)) != ChatMessageModificationKinds.Original) - SetProperty(nameof(ModificationKind), ChatMessageModificationKinds.Modified); - SetProperty(nameof(LastModificationTime), DateTime.Now); - } - } - - /// - /// Gets or sets the modification kind. - /// - public ChatMessageModificationKinds ModificationKind - { - get => GetCurrentProperty(); - set => SetCurrentProperty(value); - } - - /// - /// Gets the message type. - /// - public string MessageType => GetCurrentProperty(); - - /// - /// Gets the creation date time. - /// - public DateTime CreationTime => GetCurrentProperty(); - - /// - /// Gets the creation date time. - /// - public DateTime LastModificationTime - { - get => GetCurrentProperty(); - set => SetCurrentProperty(value); - } - - /// - /// Gets the category. - /// - public string Category - { - get => GetCurrentProperty(); - set => SetCurrentProperty(value); - } - - /// - /// Gets the additional data. - /// - public JsonObjectNode Info { get; } - - /// - /// Converts to JSON object. - /// - /// A JSON object. - public override JsonObjectNode ToJson() - { - var json = base.ToJson(); - json.SetValue("sender", Sender); - json.SetValue("text", Message); - json.SetValue("created", CreationTime); - json.SetValue("modified", LastModificationTime); - json.SetValue("type", MessageType); - if (Info.Count > 0) json.SetValue("info", Info); - if (!string.IsNullOrWhiteSpace(Category)) json.SetValue("category", Category); - var data = GetProperty("Data"); - if (data == null) return json; - if (data is JsonObjectNode j) - { - json.SetValue("data", j); - return json; - } - - if (data is IJsonObjectHost joh) - { - json.SetValue("data", joh); - return json; - } - - try - { - var d = JsonSerializer.Serialize(data); - if (string.IsNullOrWhiteSpace(d)) return json; - json.SetValue("data", JsonObjectNode.ConvertFrom(d)); - return json; - } - catch (ArgumentException) - { - } - catch (JsonException) - { - } - catch (NotSupportedException) - { - } - catch (InvalidOperationException) - { - } - - return json; - } - - /// - /// Returns a string which represents the object instance. - /// - /// A string which represents the object instance. - public override string ToString() - { - var nickname = Sender?.Nickname?.Trim(); - if (string.IsNullOrEmpty(nickname)) nickname = "?"; - return $"{nickname} ({LastModificationTime}){Environment.NewLine}{Message}"; - } - - /// - /// Converts the JSON raw back. - /// - /// The source value. - /// A model of the message. - public static implicit operator ExtendedChatMessage(JsonObjectNode value) - { - if (value is null) return null; - var type = value.TryGetStringTrimmedValue("type", true)?.ToLowerInvariant(); - if (type == null) return new(value); - return type switch - { - ExtendedChatMessages.AttachmentLinkItemKey => new ExtendedChatMessage(value, json => new AttachmentLinkItem(json), type), - ExtendedChatMessages.AttachmentLinkSetKey => new ExtendedChatMessage(value, json => new AttachmentLinkSet(json), type), - ExtendedChatMessages.MarkdownKey or "text\\md" or "text/markdown" or "markdown" => new ExtendedChatMessage(value, json => new ExtendedChatMessageTextData(json), ExtendedChatMessages.MarkdownKey), - _ => new(value) - }; - } - - /// - /// Converts to JSON object. - /// - /// The JSON value. - /// A JSON object. - public static explicit operator JsonObjectNode(ExtendedChatMessage value) - => value?.ToJson(); -} - -/// -/// The chat message record. -/// -public class ExtendedChatMessage : ExtendedChatMessage where T : class -{ - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The message type. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - protected ExtendedChatMessage(string type, UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) - : this(type, Guid.NewGuid(), sender, data, message, creation, info) - { - } - - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The message type. - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - protected ExtendedChatMessage(string type, Guid id, UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) - : this(type, ExtendedChatMessages.ToIdString(id), sender, data, message, creation, info) - { - } - - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The message type. - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - protected internal ExtendedChatMessage(string type, string id, UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) - : base(id, sender, message, creation, info, type) - { - Data = data; - } - - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The JSON object to parse. - /// true if skip to deserialize the data; otherwise, false. - /// The message type to override; or null, if use the one in JSON input. - protected ExtendedChatMessage(JsonObjectNode json, bool ignoreDataDeserialize = false, string type = null) - : base(json) - { - if (!string.IsNullOrWhiteSpace(type)) SetProperty(nameof(MessageType), type); - if (ignoreDataDeserialize || json == null) return; - var data = json.TryGetObjectValue("data"); - if (data == null) return; - Data = data.Deserialize(); - } - - /// - /// Initializes a new instance of the ExtendedChatMessage class. - /// - /// The JSON object to parse. - /// The data converter. - /// The message type to override; or null, if use the one in JSON input. - protected internal ExtendedChatMessage(JsonObjectNode json, Func dataConverter, string type = null) - : base(json) - { - if (!string.IsNullOrWhiteSpace(type)) SetProperty(nameof(MessageType), type); - var data = json?.TryGetObjectValue("data"); - if (data == null) - { - var s = json.TryGetStringTrimmedValue("data", true); - if (s == null || dataConverter == null) return; - if (s.StartsWith("{") && s.StartsWith("}")) data = JsonObjectNode.TryParse(s); - else if (s.StartsWith("[") || s.StartsWith("<")) return; - else data = new() - { - { "value", s } - }; - } - - if (data == null) return; - Data = dataConverter == null ? data.Deserialize() : dataConverter(data); - } - - /// - /// Gets the data of customized message details. - /// - public T Data - { - get => GetCurrentProperty(); - protected set => SetCurrentProperty(value); - } - - /// - /// Converts to JSON object. - /// - /// The JSON value. - /// A JSON object. - public static explicit operator JsonObjectNode(ExtendedChatMessage value) - => value?.ToJson(); -} - -/// -/// The rich text data for chat message. -/// -public class ExtendedChatMessageTextData : BaseObservableProperties, IJsonObjectHost -{ - /// - /// Initializes a new instance of the ExtendedChatMessageTextData class. - /// - public ExtendedChatMessageTextData() - { - Info = new(); - } - - /// - /// Initializes a new instance of the ExtendedChatMessageTextData class. - /// - /// The rich text. - public ExtendedChatMessageTextData(string value) - { - Value = value; - Info = new(); - } - - /// - /// Initializes a new instance of the ExtendedChatMessageTextData class. - /// - /// The JSON object to parse. - public ExtendedChatMessageTextData(JsonObjectNode json) - { - if (json == null) return; - Value = json.TryGetStringValue("value"); - Info = json.TryGetObjectValue("info") ?? new(); - } - - /// - /// Gets or sets the value. - /// - public string Value - { - get => GetCurrentProperty(); - set => SetCurrentProperty(value); - } - - /// - /// Gets the additional information. - /// - public JsonObjectNode Info { get; } - - /// - /// Converts to JSON object. - /// - /// A JSON object. - public virtual JsonObjectNode ToJson() - { - var json = new JsonObjectNode - { - { "value", Value } - }; - if (Info.Count > 0) json.SetValue("info", Info); - return json; - } - - /// - /// Returns a string which represents the object instance. - /// - /// A string which represents the object instance. - public override string ToString() - => Value; - - /// - /// Converts the JSON raw back. - /// - /// The source value. - /// A model of the message. - public static implicit operator ExtendedChatMessageTextData(JsonObjectNode value) - => new(value); - - /// - /// Converts to JSON object. - /// - /// The JSON value. - /// A JSON object. - public static explicit operator JsonObjectNode(ExtendedChatMessageTextData value) - => value?.ToJson(); -} - -/// -/// The factory of the extended chat message data. -/// -/// The type of data. -public abstract class BaseExtendedChatMessageDataFactory where T : class -{ - /// - /// Gets the message type. - /// - public string MessageType { get; } - - /// - /// Generates the data instance from a JSON object. - /// - /// The JSON object node input. - /// The instance of the data. - public virtual T Create(JsonObjectNode json) - => json == null ? default : json.Deserialize(); - - /// - /// Occurs on the message is created. - /// - /// The chat message created. - protected virtual void OnCreateMessage(ExtendedChatMessage message) - { - } - - /// - /// Creates a chat message record. - /// - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public ExtendedChatMessage CreateMessage(UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) - => CreateMessage(Guid.NewGuid(), sender, data, message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public ExtendedChatMessage CreateMessage(Guid id, UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) - => CreateMessage(ExtendedChatMessages.ToIdString(id), sender, data, message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public ExtendedChatMessage CreateMessage(string id, UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) - { - if (string.IsNullOrWhiteSpace(MessageType)) return null; - var obj = new ExtendedChatMessage(MessageType, id, sender, data, message, creation, info); - OnCreateMessage(obj); - return obj; - } - - /// - /// Creates a chat message record. - /// - /// The JSON object to parse. - /// The chat message. - public ExtendedChatMessage CreateMessage(JsonObjectNode json) - { - var obj = new ExtendedChatMessage(json, Create, MessageType); - if (string.IsNullOrWhiteSpace(obj.MessageType)) return null; - OnCreateMessage(obj); - return obj; - } -} - -/// -/// The helper of extended chat message. -/// -public static class ExtendedChatMessages -{ - internal const string AttachmentLinkItemKey = "attachment\\item"; - internal const string AttachmentLinkSetKey = "attachment\\list"; - internal const string MarkdownKey = "text\\markdown"; - - /// - /// Creates a chat message record. - /// - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(UserItemInfo sender, AttachmentLinkItem data, string message, DateTime? creation = null, JsonObjectNode info = null) - => Create(Guid.NewGuid(), sender, data, message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(Guid id, UserItemInfo sender, AttachmentLinkItem data, string message, DateTime? creation = null, JsonObjectNode info = null) - => Create(ToIdString(id), sender, data, message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(string id, UserItemInfo sender, AttachmentLinkItem data, string message, DateTime? creation = null, JsonObjectNode info = null) - => new(AttachmentLinkItemKey, id, sender, data, message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The JSON object to parse. - /// The chat message. - public static ExtendedChatMessage CreateAttachmentLinkItem(JsonObjectNode json) - => new(json, data => new AttachmentLinkItem(data), AttachmentLinkItemKey); - - /// - /// Creates a chat message record. - /// - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(UserItemInfo sender, AttachmentLinkSet data, string message, DateTime? creation = null, JsonObjectNode info = null) - => Create(Guid.NewGuid(), sender, data ?? new(), message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(Guid id, UserItemInfo sender, AttachmentLinkSet data, string message, DateTime? creation = null, JsonObjectNode info = null) - => Create(ToIdString(id), sender, data ?? new(), message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(string id, UserItemInfo sender, AttachmentLinkSet data, string message, DateTime? creation = null, JsonObjectNode info = null) - => new(AttachmentLinkSetKey, id, sender, data ?? new(), message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The JSON object to parse. - /// The chat message. - public static ExtendedChatMessage CreateAttachmentLinkSet(JsonObjectNode json) - => new(json, data => new AttachmentLinkSet(data), AttachmentLinkSetKey); - - /// - /// Creates a chat message record. - /// - /// The data factory. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(BaseExtendedChatMessageDataFactory factory, UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) where T : class - => factory?.CreateMessage(Guid.NewGuid(), sender, data, message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The data factory. - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(BaseExtendedChatMessageDataFactory factory, Guid id, UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) where T : class - => factory?.CreateMessage(ToIdString(id), sender, data, message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The data factory. - /// The message identifier. - /// The nickname of the sender. - /// The message data. - /// The message text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage Create(BaseExtendedChatMessageDataFactory factory, string id, UserItemInfo sender, T data, string message, DateTime? creation = null, JsonObjectNode info = null) where T : class - => factory?.CreateMessage(id, sender, data, message, creation, info); - - /// - /// Creates a chat message record. - /// - /// The data factory. - /// The JSON object to parse. - /// The chat message. - public static ExtendedChatMessage Create(BaseExtendedChatMessageDataFactory factory, JsonObjectNode json) where T : class - => factory?.CreateMessage(json); - - /// - /// Creates a chat message record. - /// - /// The lowercase name of the programming language without whitespace. - /// The nickname of the sender. - /// The code snippet. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage CreateCodeSnippet(string languageName, UserItemInfo sender, string codeSnippet, DateTime? creation = null, JsonObjectNode info = null) - => new(Guid.NewGuid(), sender, codeSnippet, creation, info, string.Concat("code\\", languageName)); - - /// - /// Creates a chat message record. - /// - /// The lowercase name of the programming language without whitespace. - /// The message identifier. - /// The nickname of the sender. - /// The code snippet. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage CreateCodeSnippet(string languageName, Guid id, UserItemInfo sender, string codeSnippet, DateTime? creation = null, JsonObjectNode info = null) - => new(id, sender, codeSnippet, creation, info, string.Concat("code\\", languageName)); - - /// - /// Creates a chat message record. - /// - /// The lowercase name of the programming language without whitespace. - /// The message identifier. - /// The nickname of the sender. - /// The code snippet. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage CreateCodeSnippet(string languageName, string id, UserItemInfo sender, string codeSnippet, DateTime? creation = null, JsonObjectNode info = null) - => new(id, sender, codeSnippet, creation, info, string.Concat("code\\", languageName)); - - /// - /// Creates a chat message record. - /// - /// The nickname of the sender. - /// The markdown text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage CreateMarkdown(UserItemInfo sender, string markdown, DateTime? creation = null, JsonObjectNode info = null) - => CreateMarkdown(Guid.NewGuid(), sender, markdown, creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The markdown text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage CreateMarkdown(Guid id, UserItemInfo sender, string markdown, DateTime? creation = null, JsonObjectNode info = null) - => CreateMarkdown(id, sender, new ExtendedChatMessageTextData(markdown), creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The markdown text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage CreateMarkdown(string id, UserItemInfo sender, string markdown, DateTime? creation = null, JsonObjectNode info = null) - => CreateMarkdown(id, sender, new ExtendedChatMessageTextData(markdown), creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The markdown text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage CreateMarkdown(Guid id, UserItemInfo sender, ExtendedChatMessageTextData markdown, DateTime? creation = null, JsonObjectNode info = null) - => CreateMarkdown(ToIdString(id), sender, markdown, creation, info); - - /// - /// Creates a chat message record. - /// - /// The message identifier. - /// The nickname of the sender. - /// The markdown text. - /// The creation date time; or null if use now. - /// The additional information; or null if create a new one. - /// The chat message. - public static ExtendedChatMessage CreateMarkdown(string id, UserItemInfo sender, ExtendedChatMessageTextData markdown, DateTime? creation = null, JsonObjectNode info = null) - => new(MarkdownKey, id, sender, markdown, null, creation, info); - - /// - /// Creates a chat message record. - /// - /// The JSON object to parse. - /// The chat message. - public static ExtendedChatMessage CreateMarkdown(JsonObjectNode json) - => new(json, data => new ExtendedChatMessageTextData(data), MarkdownKey); - - /// - /// Tests if the message contains an attachment item. - /// - /// The chat message. - /// true if contains; otherwise, false. - public static bool HasAttachment(this ExtendedChatMessage message) - => message?.Data?.Link != null; - - /// - /// Tests if contains the specific item. - /// - /// The chat message. - /// The attachment item. - /// true if contains; otherwise, false. - public static bool ContainsItem(this ExtendedChatMessage message, AttachmentLinkItem item) - => message != null && message.Data.Contains(item); - - /// - /// Tests if contains the specific item. - /// - /// The chat message. - /// The attachment link. - /// true if contains; otherwise, false. - public static bool ContainsItem(this ExtendedChatMessage message, Uri link) - => message != null && message.Data.Contains(link); - - /// - /// Tries to get the specific item. - /// - /// The chat message. - /// The zero-base index. - /// The attachment item; or null, if the index is not valid. - public static AttachmentLinkItem TryGetItem(this ExtendedChatMessage message, int index) - => message?.Data.TryGet(index); - - /// - /// Adds an attachment. - /// - /// The chat message. - /// The attachment to add. - public static void AddItem(this ExtendedChatMessage message, AttachmentLinkItem item) - => message?.Data.Add(item); - - /// - /// Adds an attachment. - /// - /// The chat message. - /// The URI of the attachment. - /// The MIME value of the attachment. - public static AttachmentLinkItem AddItem(this ExtendedChatMessage message, Uri link, string mime) - => message?.Data.Add(link, mime); - - /// - /// Adds an attachment. - /// - /// The chat message. - /// The URI of the attachment. - /// The MIME value of the attachment. - /// The name of the attachment. - /// The thumbnail URI of the attachment. - public static AttachmentLinkItem AddItem(this ExtendedChatMessage message, Uri link, string mime, string name, Uri thumbnail) - => message?.Data.Add(link, mime, name, thumbnail); - - /// - /// Adds an attachment. - /// - /// The chat message. - /// The attachment items to add. - /// The count of the item added. - public static int AddItems(this ExtendedChatMessage message, IEnumerable items) - => message?.Data.AddRange(items) ?? 0; - - /// - /// Adds an attachment. - /// - /// The chat message. - /// The zero-based index to insert the specific item. - /// The attachment to add. - /// The index is not valid. - public static void InsertItem(this ExtendedChatMessage message, int index, AttachmentLinkItem item) - => message?.Data.Insert(index, item); - - /// - /// Adds an attachment. - /// - /// The chat message. - /// The zero-based index to insert the specific item. - /// The URI of the attachment. - /// The MIME value of the attachment. - /// The index is not valid. - public static AttachmentLinkItem InsertItem(this ExtendedChatMessage message, int index, Uri link, string mime) - => message?.Data.Insert(index, link, mime); - - /// - /// Adds an attachment. - /// - /// The chat message. - /// The zero-based index to insert the specific item. - /// The URI of the attachment. - /// The MIME value of the attachment. - /// The name of the attachment. - /// The thumbnail URI of the attachment. - /// The index is not valid. - public static AttachmentLinkItem InsertItem(this ExtendedChatMessage message, int index, Uri link, string mime, string name, Uri thumbnail) - => message?.Data.Insert(index, link, mime, name, thumbnail); - - /// - /// Tests if the message is in plain text. - /// - /// The message. - /// Try if the message body is plain text; otherwise, false. - public static bool IsPlainTextMessage(ExtendedChatMessage message) - { - if (message == null) return false; - var type = message.MessageType?.ToLowerInvariant(); - if (string.IsNullOrEmpty(type)) return true; - return type == "text" || type == "txt" || type == "text\\plain" || type == "text/plain"; - } - - /// - /// Removes a specific attachment. - /// - /// The chat message. - /// The attachment to remove. - /// true if item is found and successfully removed; otherwise, false. - public static bool RemoveItem(this ExtendedChatMessage message, AttachmentLinkItem item) - => message != null && message.Data.Remove(item); - - internal static string ToIdString(Guid id) - => id.ToString("N"); -} diff --git a/Messages/Text/ExtendedChatThread.cs b/Messages/Text/ExtendedChatThread.cs deleted file mode 100644 index cffe7ce1..00000000 --- a/Messages/Text/ExtendedChatThread.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; -using Trivial.Collection; -using Trivial.Reflection; -using Trivial.Tasks; -using Trivial.Users; - -namespace Trivial.Text; - -/// -/// The chat message thread, may be a user, group, bot or topic. -/// -public interface IExtendedChatThread -{ - /// - /// Gets the identifier. - /// - string Id { get; } - - /// - /// Gets the nickname. - /// - string Nickname { get; } - - /// - /// Gets the URI of the avatar. - /// - Uri AvatarUri { get; } - - /// - /// Gets a value indicating whether the thread is read only for current user. - /// - bool IsReadOnly { get; } - - /// - /// Gets a value indicating whether the thread can contain multiple participators (the current user and more than 1 other participators). - /// - bool IsMultipleParticipatorsMode { get; } - - /// - /// Gets a value indicating whether the thread is round mode to send message. - /// - bool IsRoundMode { get; } -} - -/// -/// The chat message thread, may be a user, group, bot or topic. -/// -public abstract class BaseExtendedChatThread : BaseObservableProperties, IExtendedChatThread -{ - /// - /// Initializes a new instance of the BaseExtendedChatThread class. - /// - /// The identifier. - /// The nickname. - /// The avatar. - /// true if the thread is read-only for the current user; otherwise, false. - /// true if the thread can contain multiple participators (the current user and more than 1 other participators); otherwise, false. - /// true if the current user can only send message one by one when the participator is available to receive; otherwise, false. - public BaseExtendedChatThread(string id, string nickname, Uri avatar, bool isReadOnly = false, bool isMultipleParticipatorsMode = false, bool isRoundMode = false) - { - SetProperty(nameof(Id), id); - SetProperty(nameof(Nickname), nickname); - SetProperty(nameof(AvatarUri), avatar); - SetProperty(nameof(IsReadOnly), isReadOnly); - SetProperty(nameof(IsMultipleParticipatorsMode), isMultipleParticipatorsMode); - SetProperty(nameof(IsRoundMode), isRoundMode); - } - - /// - /// Gets the identifier. - /// - public virtual string Id => GetCurrentProperty(); - - /// - /// Gets the nickname. - /// - public virtual string Nickname => GetCurrentProperty(); - - /// - /// Gets the URI of the avatar. - /// - public virtual Uri AvatarUri => GetCurrentProperty(); - - /// - /// Gets a value indicating whether the thread is read-only for current user. - /// - public virtual bool IsReadOnly => GetCurrentProperty(); - - /// - /// Gets a value indicating whether the thread can contain multiple participators (the current user and more than 1 other participators). - /// - public virtual bool IsMultipleParticipatorsMode => GetCurrentProperty(); - - /// - /// Gets a value indicating whether the current user can only send message one by one when the participator is available to receive. - /// - public virtual bool IsRoundMode => GetCurrentProperty(); -} - -/// -/// The chat message thread, may be a user, group, bot or topic. -/// -public class UserExtendedChatThread : IExtendedChatThread, INotifyPropertyChanged -{ - private readonly Dictionary toggles = new(); - private readonly Dictionary flags = new(); - - /// - /// Initializes a new instance of the UserExtendedChatThread class. - /// - /// - public UserExtendedChatThread(UserItemInfo user) - { - User = user ?? new(); - User.PropertyChanged += OnUserPropertyChanged; - } - - /// - /// Deconstructor. - /// - ~UserExtendedChatThread() - { - var user = User; - if (user == null) return; - user.PropertyChanged -= OnUserPropertyChanged; - } - - /// - /// Adds or removes the event handler raised on property changed. - /// - public event PropertyChangedEventHandler PropertyChanged; - - /// - /// Gets the user. - /// - public UserItemInfo User { get; } - - /// - /// Gets the identifier. - /// - public virtual string Id => User.Id; - - /// - /// Gets the nickname. - /// - public virtual string Nickname => User.Nickname; - - /// - /// Gets the URI of the avatar. - /// - public virtual Uri AvatarUri => User.AvatarUri; - - /// - /// Gets the gender. - /// - public virtual Genders Gender => User.Gender; - - /// - /// Gets the security entity type. - /// - public virtual SecurityEntityTypes SecurityEntityType => User.SecurityEntityType; - - /// - /// Gets a value indicating whether the thread is read-only for current user. - /// - public virtual bool IsReadOnly => GetBooleanFlag(nameof(IsReadOnly)) ?? false; - - /// - /// Gets a value indicating whether the thread can contain multiple participators (the current user and more than 1 other participators). - /// - public virtual bool IsMultipleParticipatorsMode => GetBooleanFlag(nameof(IsMultipleParticipatorsMode)) ?? false; - - /// - /// Gets a value indicating whether the current user can only send message one by one when the participator is available to receive. - /// - public virtual bool IsRoundMode => GetBooleanFlag(nameof(IsRoundMode)) ?? false; - - /// - /// Sets a flag. - /// - /// The property key. - /// The value of the property. - protected void SetFlag(string key, string value) - { - flags[key] = value; - PropertyChanged?.Invoke(this, new(key)); - } - - /// - /// Sets a flag. - /// - /// The property key. - /// The value of the property. - protected void SetFlag(string key, bool value) - { - toggles[key] = value; - flags[key] = value ? JsonBooleanNode.TrueString : JsonBooleanNode.FalseString; - PropertyChanged?.Invoke(this, new(key)); - } - - /// - /// Sets a flag to false. - /// - /// The property key. - protected void SetFalse(string key) - => SetFlag(key, false); - - /// - /// Sets a flag to true. - /// - /// The property key. - protected void SetTrue(string key) - => SetFlag(key, true); - - /// - /// Gets the specific flag. - /// - /// The property key. - /// The value of the property. - protected string GetFlag(string key) - => flags.TryGetValue(key, out var s) ? s : null; - - /// - /// Gets the specific flag. - /// - /// The property key. - /// true if the flag value is true; false if the flag value is false; null if does not exists or is not a boolean flag. - protected bool? GetBooleanFlag(string key) - => toggles.TryGetValue(key, out var b) ? b : null; - - private void OnUserPropertyChanged(object sender, PropertyChangedEventArgs e) - => PropertyChanged?.Invoke(this, e); -} - -/// -/// The chat message thread, may be a user, group, bot or topic. -/// -public class CommandGuidanceExtendedChatThread : UserExtendedChatThread -{ - /// - /// Initializes a new instance of the CommandGuidanceExtendedChatThread class. - /// - /// The identifier. - /// The chat command guidance client. - /// The display name. - /// The avatar URI. - public CommandGuidanceExtendedChatThread(string id, BaseChatCommandGuidanceClient client, string nickname, Uri avatar) - : base(new(id, nickname, Genders.Machine, avatar)) - { - Client = client; - SetTrue(nameof(IsRoundMode)); - } - - /// - /// Gets the chat command guidance client. - /// - public BaseChatCommandGuidanceClient Client { get; } -} \ No newline at end of file diff --git a/Messages/Text/JsonProperty.cs b/Messages/Text/JsonProperty.cs deleted file mode 100644 index 82576f6f..00000000 --- a/Messages/Text/JsonProperty.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Trivial.Text; - -/// -/// The locale property resolver of JSON object. -/// -public class LocaleJsonPropertyResolver : IJsonPropertyResolver -{ - /// - /// Gets or sets the optional current market code. - /// - public string Market { get; set; } - - /// - /// Gets or sets the optional prefix of property key. - /// - public string Prefix { get; set; } - - /// - /// Gets or sets the optional suffix of property key. - /// - public string Suffix { get; set; } - - /// - /// Gets or sets the optional fallback market code. - /// - public string Fallback { get; set; } - - /// - /// Gets the property value. - /// - /// The source node. - /// The value of the property. - /// true if has the property and the type is the one expected; otherwise, false. - public bool TryGetValue(JsonObjectNode node, out string result) - { - var mkt = string.IsNullOrWhiteSpace(Market) ? Market.Trim() : CultureInfo.CurrentUICulture?.Name?.Trim(); - if (node == null || string.IsNullOrEmpty(mkt)) - { - result = null; - return false; - } - - string key; - string name; - while (true) - { - key = $"{Prefix}{mkt}{Suffix}"; - name = node.TryGetStringValue(key)?.Trim(); - if (!string.IsNullOrEmpty(name)) - { - result = name; - return true; - } - - key = $"{Prefix}{mkt.ToLowerInvariant()}{Suffix}"; - name = node.TryGetStringValue(key)?.Trim(); - if (!string.IsNullOrEmpty(name)) - { - result = name; - return true; - } - - var i = mkt.LastIndexOf('-'); - #pragma warning disable IDE0057 - if (i > 0) mkt = mkt.Substring(0, i); - #pragma warning restore IDE0057 - else break; - } - - key = $"{Prefix}{Fallback ?? "ww"}{Suffix}"; - result = node.TryGetStringValue(key)?.Trim(); - return string.IsNullOrEmpty(result); - } -} diff --git a/Messages/Users/BaseUserGroupItemInfo.cs b/Messages/Users/BaseUserGroupItemInfo.cs deleted file mode 100644 index 3af38d47..00000000 --- a/Messages/Users/BaseUserGroupItemInfo.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; -using Trivial.Data; -using Trivial.Reflection; -using Trivial.Tasks; -using Trivial.Text; - -namespace Trivial.Users; - -/// -/// The user group item information. -/// -public class BaseUserGroupItemInfo : BaseSecurityEntityInfo -{ - /// - /// Initializes a new instance of the BaseUserGroupItemInfo class. - /// - public BaseUserGroupItemInfo() - : base(SecurityEntityTypes.Group) - { - } - - /// - /// Initializes a new instance of the BaseUserGroupItemInfo class. - /// - public BaseUserGroupItemInfo(string id, string nickname, Uri avatar = null) - : base(SecurityEntityTypes.Group, id, nickname, avatar) - { - } - - /// - /// Initializes a new instance of the BaseUserGroupItemInfo class. - /// - /// The JSON object to parse. - public BaseUserGroupItemInfo(JsonObjectNode json) - : base(SecurityEntityTypes.Group, json) - { - if (json == null) return; - DefaultMembershipPolicy = json.TryGetEnumValue("memberPolicy") ?? UserGroupMembershipPolicies.Forbidden; - } - - /// - /// Gets or sets the membership policy. - /// - public UserGroupMembershipPolicies DefaultMembershipPolicy - { - get => GetCurrentProperty(); - set => SetCurrentProperty(value); - } - - /// - /// Converts to JSON object. - /// - /// A JSON object. - public override JsonObjectNode ToJson() - { - var json = base.ToJson(); - if (DefaultMembershipPolicy != 0) json.SetValue("memberPolicy", DefaultMembershipPolicy.ToString()); - return json; - } - - /// - /// Converts the JSON raw back. - /// - /// The source value. - /// The request instance. - public static implicit operator BaseUserGroupItemInfo(JsonObjectNode value) - => value is null ? null : new(value); -} diff --git a/Messages/Users/Genders.cs b/Messages/Users/Genders.cs deleted file mode 100644 index ebc623a7..00000000 --- a/Messages/Users/Genders.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Trivial.Users; - -/// -/// Genders. -/// -public enum Genders : byte -{ - /// - /// Unknown (or secret). - /// - Unknown = 0, - - /// - /// Male. - /// - Male = 1, - - /// - /// Female. - /// - Female = 2, - - /// - /// Asexual. - /// - Asexual = 5, - - /// - /// Machine. - /// - Machine = 6, - - /// - /// Other. - /// - Other = 7 -} diff --git a/Messages/Users/SecurityEntityInfo.cs b/Messages/Users/SecurityEntityInfo.cs deleted file mode 100644 index ff66e291..00000000 --- a/Messages/Users/SecurityEntityInfo.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using Trivial.Data; -using Trivial.Reflection; -using Trivial.Tasks; -using Trivial.Text; - -namespace Trivial.Users; - -/// -/// The user item information. -/// -public abstract class BaseSecurityEntityInfo : BaseResourceEntityInfo -{ - /// - /// Initializes a new instance of the BaseSecurityEntityInfo class. - /// - /// The security entity type. - protected BaseSecurityEntityInfo(SecurityEntityTypes type) - { - SecurityEntityType = type; - } - - /// - /// Initializes a new instance of the BaseSecurityEntityInfo class. - /// - /// The security entity type. - /// The resource identifier. - /// The nickname or display name. - /// The avatar URI. - protected BaseSecurityEntityInfo(SecurityEntityTypes type, string id, string nickname, Uri avatar = null) - : this(type) - { - Id = id; - Nickname = nickname; - AvatarUri = avatar; - } - - /// - /// Initializes a new instance of the BaseSecurityEntityInfo class. - /// - /// The security entity type. - /// The JSON object to parse. - protected BaseSecurityEntityInfo(SecurityEntityTypes type, JsonObjectNode json) - : this(type) - { - if (json == null) return; - Id = json.TryGetStringTrimmedValue("id", true) ?? json.Id; - Nickname = json.TryGetStringTrimmedValue("nickname", true); - AvatarUri = json.TryGetUriValue("avatar"); - if (json.TryGetBooleanValue("_raw") != false) SetProperty("_raw", json); - } - - /// - /// Initializes a new instance of the BaseSecurityEntityInfo class. - /// - /// The JSON object to parse. - /// The security entity type converter. - /// The default security entity type. - protected BaseSecurityEntityInfo(JsonObjectNode json, Func typeConverter, SecurityEntityTypes defaultType = SecurityEntityTypes.Unknown) - { - if (json == null) - { - SecurityEntityType = defaultType; - return; - } - - SecurityEntityType = typeConverter?.Invoke(json) ?? defaultType; - Id = json.TryGetStringTrimmedValue("id", true) ?? json.Id; - Nickname = json.TryGetStringTrimmedValue("nickname", true) ?? json.TryGetStringTrimmedValue("name", true); - AvatarUri = json.TryGetUriValue("avatar") ?? json.TryGetUriValue("icon"); - if (json.TryGetBooleanValue("_raw") != false) SetProperty("_raw", json); - } - - /// - /// Gets the security entity type. - /// - public SecurityEntityTypes SecurityEntityType { get; } - - /// - /// Gets or sets the nickname. - /// - public string Nickname - { - get => GetCurrentProperty(); - set => SetCurrentProperty(value); - } - - /// - /// Gets or sets the URI of avatar. - /// - public Uri AvatarUri - { - get => GetCurrentProperty(); - set => SetCurrentProperty(value); - } - - /// - /// Gets the raw JSON object for reference. - /// The value is null if no such data. - /// - protected JsonObjectNode RawJson => base.GetProperty("_raw"); - - /// - /// Converts to JSON object. - /// - /// A JSON object. - public override JsonObjectNode ToJson() - { - var json = base.ToJson(); - json.SetValue("nickname", Nickname); - json.SetValue("avatar", AvatarUri); - return json; - } - - /// - /// Gets a property value. - /// - /// The type of the property value. - /// The key. - /// The default value. - /// A property value. - public new T GetProperty(string key, T defaultValue = default) - => base.GetProperty(key, defaultValue); - - /// - /// Gets a property value. - /// - /// The type of the property value. - /// The key. - /// The property value. - /// true if contains; otherwise, false. - public new bool GetProperty(string key, out T result) - => base.GetProperty(key, out result); - - /// - /// Converts to JSON object. - /// - /// The JSON value. - /// A JSON object. - public static explicit operator JsonObjectNode(BaseSecurityEntityInfo value) - => value?.ToJson(); -} diff --git a/Messages/Users/SecurityEntityTypes.cs b/Messages/Users/SecurityEntityTypes.cs deleted file mode 100644 index a826fa48..00000000 --- a/Messages/Users/SecurityEntityTypes.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Runtime.Serialization; -using System.Security; -using System.Security.Principal; -using System.Text; -using System.Text.Json.Serialization; - -using Trivial.Security; -using Trivial.Text; - -namespace Trivial.Users -{ - /// - /// Security entity types. - /// - public enum SecurityEntityTypes : byte - { - /// - /// Unknown. - /// - Unknown = 0, - - /// - /// User. - /// - User = 1, - - /// - /// User group. - /// - Group = 2, - - /// - /// Service. - /// - Service = 3, - - /// - /// Bot. - /// - Bot = 4, - - /// - /// The special agent. - /// - Agent = 6, - } -} diff --git a/Messages/Users/UserGroupMembershipPolicies.cs b/Messages/Users/UserGroupMembershipPolicies.cs deleted file mode 100644 index de21f459..00000000 --- a/Messages/Users/UserGroupMembershipPolicies.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Trivial.Users; - -/// -/// User group membership policies. -/// -public enum UserGroupMembershipPolicies -{ - /// - /// Disallow to join in. - /// - Forbidden = 0, - - /// - /// Need apply for membership with approval. - /// - Application = 1, - - /// - /// Allow to join in directly. - /// - Allow = 2, -} diff --git a/Messages/Users/UserItemInfo.cs b/Messages/Users/UserItemInfo.cs deleted file mode 100644 index 55314f03..00000000 --- a/Messages/Users/UserItemInfo.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using Trivial.Data; -using Trivial.Reflection; -using Trivial.Tasks; -using Trivial.Text; - -namespace Trivial.Users; - -/// -/// The user item information. -/// -public class UserItemInfo : BaseSecurityEntityInfo -{ - /// - /// Initializes a new instance of the UserItemInfo class. - /// - public UserItemInfo() - : base(SecurityEntityTypes.User) - { - } - - /// - /// Initializes a new instance of the UserItemInfo class. - /// - public UserItemInfo(string id, string nickname, Genders gender = Genders.Unknown, Uri avatar = null) - : base(gender == Genders.Machine ? SecurityEntityTypes.Bot : SecurityEntityTypes.User, id, nickname, avatar) - { - Gender = gender; - } - - /// - /// Initializes a new instance of the UserItemInfo class. - /// - /// The JSON object to parse. - public UserItemInfo(JsonObjectNode json) - : base(json, GetSecurityEntityType, SecurityEntityTypes.User) - { - if (json == null) return; - Gender = json.TryGetEnumValue("gender") ?? Genders.Unknown; - } - - /// - /// Gets or sets the gender. - /// - public Genders Gender - { - get => GetCurrentProperty(); - set => SetCurrentProperty(value); - } - - /// - /// Converts to JSON object. - /// - /// A JSON object. - public override JsonObjectNode ToJson() - { - var json = base.ToJson(); - json.SetValue("gender", Gender.ToString()); - return json; - } - - /// - /// Converts the JSON raw back. - /// - /// The source value. - /// The request instance. - public static implicit operator UserItemInfo(JsonObjectNode value) - => value is null ? null : new(value); - - /// - /// Converts to JSON object. - /// - /// The JSON value. - /// A JSON object. - public static explicit operator JsonObjectNode(UserItemInfo value) - => value?.ToJson(); - - private static SecurityEntityTypes GetSecurityEntityType(JsonObjectNode json) - { - var type = json?.TryGetStringTrimmedValue("gender", true)?.ToLowerInvariant(); - if (type == null) return SecurityEntityTypes.User; - return type switch - { - "bot" or "robot" or "machine" or "6" => SecurityEntityTypes.Bot, - "agent" => SecurityEntityTypes.Agent, - _ => SecurityEntityTypes.User - }; - } -} diff --git a/Messages/Web/AudioMimeConstants.cs b/Messages/Web/AudioMimeConstants.cs deleted file mode 100644 index a057e14f..00000000 --- a/Messages/Web/AudioMimeConstants.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The popular MIME constants of audio. - /// - public static class Audio - { - /// - /// The MIME content type of basic audio and sound. - /// - public const string Au = "audio/basic"; - - /// - /// The MIME content type of apt-X (Audio Processing Technology). - /// - public const string Aptx = "audio/aptx"; - - /// - /// The MIME content type of Musical Instrument Digital Interface. - /// - public const string Midi = "audio/midi"; - - /// - /// The MIME content type of MPEG-4. - /// - public const string Mp4 = "audio/mp4"; - - /// - /// The MIME content type of MPEG-1 audio layer 3. - /// - public const string Mp3 = "audio/mpeg"; - - /// - /// The MIME content type of OGG Vobis (by Xiph.Org Foundation). - /// - public const string Ogg = "audio/ogg"; - - /// - /// The MIME content type of opus (by Xiph.Org Foundation). - /// - public const string Opus = "audio/opus"; - - /// - /// The MIME content type of WebA (by Google). - /// - public const string Weba = "audio/webm"; - - /// - /// The MIME content type of Advanced Audio Coding. - /// - public const string Aac = "audio/x-aac"; - - /// - /// The MIME content type of Audio Interchange File Format. - /// - public const string Aif = "audio/x-aiff"; - - /// - /// The MIME content type of Free Lossless Audio Codec. - /// - public const string Flac = "audio/x-flac"; - - /// - /// The MIME content type of ape (Monkey's Audio). - /// - public const string Ape = "audio/x-ape"; - - /// - /// The MIME content type of Matroska. - /// - public const string Matroska = "audio/x-matroska"; - - /// - /// The MIME content type of Windows Media Audio. - /// - public const string Wma = "audio/x-ms-wma"; - - /// - /// The MIME content type of wave audio. - /// - public const string Wav = "audio/wav"; - } -} diff --git a/Messages/Web/DocumentsMimeConstants.cs b/Messages/Web/DocumentsMimeConstants.cs deleted file mode 100644 index f89801ed..00000000 --- a/Messages/Web/DocumentsMimeConstants.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The popular MIME constants of documents. - /// - public static class Documents - { - /// - /// The MIME content type of Portable Document Format. - /// - public const string Pdf = "application/pdf"; - - /// - /// The MIME content type of ePub. - /// - public const string EPub = "application/epub+zip"; - - /// - /// The MIME content type of lotus 123. - /// - public const string Lotus123 = "vnd.lotus-1-2-3"; - - /// - /// The MIME content type of Microsoft Office Clip. - /// - public const string OfficeClip = "application/x-msclip"; - - /// - /// The MIME content type of Microsoft card file. - /// - public const string Card = "application/x-mscardfile"; - - /// - /// The MIME content type of Microsoft Office Word old binary document. - /// - public const string Doc = "application/msword"; - - /// - /// The MIME content type of Microsoft Office Word document. - /// - public const string Docx = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; - - /// - /// The MIME content type of Microsoft Office Word template. - /// - public const string Dotx = "application/vnd.openxmlformats-officedocument.wordprocessingml.template"; - - /// - /// The MIME content type of Microsoft Office Excel old binary document. - /// - public const string Xls = "application/vnd.ms-excel"; - - /// - /// The MIME content type of Microsoft Office Excel document. - /// - public const string Xlsx = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - - /// - /// The MIME content type of Microsoft Office Excel template. - /// - public const string Xltx = "application/vnd.openxmlformats-officedocument.spreadsheetml.template"; - - /// - /// The MIME content type of Microsoft Office PowerPoint old binary document. - /// - public const string Ppt = "application/vnd.ms-powerpoint"; - - /// - /// The MIME content type of Microsoft Office PowerPoint document. - /// - public const string Pptx = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; - - /// - /// The MIME content type of Microsoft Office PowerPoint SlideShow. - /// - public const string Ppsx = "application/vnd.openxmlformats-officedocument.presentationml.slideshow"; - - /// - /// The MIME content type of Microsoft Office PowerPoint slide. - /// - public const string Sldx = "application/vnd.openxmlformats-officedocument.presentationml.slide"; - - /// - /// The MIME content type of Microsoft Office PowerPoint template. - /// - public const string Potx = "application/vnd.openxmlformats-officedocument.presentationml.template"; - - /// - /// The MIME content type of Microsoft Office OneNote document. - /// - public const string OneNote = "application/onenote"; - - /// - /// The MIME content type of Microsoft Office Visio document. - /// - public const string Visio = "application/vnd.visio"; - - /// - /// The MIME content type of Microsoft Office Project document. - /// - public const string Project = "application/vnd.ms-project"; - - /// - /// The MIME content type of Microsoft Office Access database. - /// - public const string Access = "application/x-msaccess"; - - /// - /// The MIME content type of Microsoft Office Outlook file. - /// - public const string Outlook = "application/vnd.ms-outlook"; - - /// - /// The MIME content type of XML Paper Specification document. - /// - public const string Xps = "application/vnd.ms-xpsdocument"; - - /// - /// The MIME content type of Open XML Paper Specification document. - /// - public const string Oxps = "application/oxps"; - - /// - /// The MIME content type of Open Document Chart. - /// - public const string Odc = "application/vnd.oasis.opendocument.chart"; - - /// - /// The MIME content type of Open Document Chart Template. - /// - public const string Otc = "application/vnd.oasis.opendocument.chart-template"; - - /// - /// The MIME content type of Open Document Database. - /// - public const string Odb = "application/vnd.oasis.opendocument.database"; - - /// - /// The MIME content type of Open Document Formula. - /// - public const string Odf = "application/vnd.oasis.opendocument.formula"; - - /// - /// The MIME content type of Open Document Formula Template. - /// - public const string Odft = "application/vnd.oasis.opendocument.formula-template"; - - /// - /// The MIME content type of Open Document Graphics. - /// - public const string Odg = "application/vnd.oasis.opendocument.graphics"; - - /// - /// The MIME content type of Open Document Graphics Template. - /// - public const string Otg = "application/vnd.oasis.opendocument.graphics-template"; - - /// - /// The MIME content type of Open Document Image. - /// - public const string Odi = "application/vnd.oasis.opendocument.image"; - - /// - /// The MIME content type of Open Document Image Template. - /// - public const string Oti = "application/vnd.oasis.opendocument.image-template"; - - /// - /// The MIME content type of Open Document Presentation. - /// - public const string Odp = "application/vnd.oasis.opendocument.presentation"; - - /// - /// The MIME content type of Open Document Presentation Template. - /// - public const string Otp = "application/vnd.oasis.opendocument.presentation-template"; - - /// - /// The MIME content type of Open Document Spreadsheet. - /// - public const string Ods = "application/vnd.oasis.opendocument.spreadsheet"; - - /// - /// The MIME content type of Open Document Spreadsheet Template. - /// - public const string Ots = "application/vnd.oasis.opendocument.spreadsheet-template"; - - /// - /// The MIME content type of Open Document Text. - /// - public const string Odt = "application/vnd.oasis.opendocument.text"; - - /// - /// The MIME content type of Open Document Text Master. - /// - public const string Odm = "application/vnd.oasis.opendocument.text-master"; - - /// - /// The MIME content type of Open Document Text Template. - /// - public const string Ott = "application/vnd.oasis.opendocument.text-template"; - - /// - /// The MIME content type of Open Document Text Web. - /// - public const string Oth = "application/vnd.oasis.opendocument.text-web"; - } -} diff --git a/Messages/Web/ImageMimeConstants.cs b/Messages/Web/ImageMimeConstants.cs deleted file mode 100644 index e9164da7..00000000 --- a/Messages/Web/ImageMimeConstants.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The popular MIME constants of image. - /// - public static class Images - { - /// - /// The MIME content type of Animated Portable Network Graphics. - /// - public const string Apng = "image/apng"; - - /// - /// The MIME content type of Portable Network Graphics. - /// - public const string Png = "image/png"; - - /// - /// The MIME content type of bitmap (and Device-Independent Bitmap). - /// - public const string Bmp = "image/bmp"; - - /// - /// The MIME content type of AutoCAD drawings. - /// - public const string Dwg = "image/vnd.dwg"; - - /// - /// The MIME content type of Graphics Interchange Format. - /// - public const string Gif = "image/gif"; - - /// - /// The MIME content type of High Efficiency Image Format. - /// - public const string Heif = "image/heif"; - - /// - /// The MIME content type of High Efficiency Image Codec. - /// - public const string Heic = "image/heic"; - - /// - /// The MIME content type of icon and cursor. - /// - public const string Ico = "image/x-ico"; - - /// - /// The MIME content type of Joint Photographic Experts Group. - /// - public const string Jpeg = "image/jpeg"; - - /// - /// The MIME content type of Adobe Photoshop Document. - /// - public const string Psd = "image/vnd.adobe.photoshop"; - - /// - /// The MIME content type of Scalable Vector Graphics. - /// - public const string Svg = "image/svg+xml"; - - /// - /// The MIME content type of WebP (by Google). - /// - public const string Webp = "image/webp"; - - /// - /// The MIME content type of Windows Meta File. - /// - public const string Wmf = "application/x-msmetafile"; - } -} diff --git a/Messages/Web/MimeConstants.cs b/Messages/Web/MimeConstants.cs deleted file mode 100644 index c7bab1c6..00000000 --- a/Messages/Web/MimeConstants.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.Json; - -using Trivial.Collection; -using Trivial.Text; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - private static MethodInfo method; - private static KeyedDataMapping fem; - - // RFC 6838 - - /// - /// The name of MIME. - /// - public const string Name = "Multipurpose Internet Mail Extensions"; - - /// - /// The MIME content type of octet stream. - /// - public const string StreamMIME = "application/octet-stream"; - - /// - /// The MIME content type of URL encoded form. - /// - public const string FormUrlMIME = "application/x-www-form-urlencoded"; - - /// - /// Gets the MIME content type mapping of file extension. - /// - public static KeyedDataMapping FileExtensionMapping - { - get - { - if (fem != null) return fem; - var prop = typeof(WebFormat).GetProperty("MimeMapping", BindingFlags.Static | BindingFlags.NonPublic); - if (prop == null) prop = typeof(WebFormat).GetProperty("MimeMapping", BindingFlags.Static | BindingFlags.Public); - if (prop?.CanRead == true) fem = prop.GetValue(null) as KeyedDataMapping; - return fem; - } - } - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file information. - /// The MIME content type. - public static string GetByFileExtension(FileInfo file) - => GetByFileExtension(file?.Extension, StreamMIME); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file information. - /// true if returns null if not supported; otherwise, false. - /// The MIME content type. - public static string GetByFileExtension(FileInfo file, bool returnNullIfUnsupported) - => GetByFileExtension(file?.Extension, returnNullIfUnsupported ? null : StreamMIME); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file information. - /// The default MIME content type. - /// The MIME content type. - public static string GetByFileExtension(FileInfo file, string defaultMime) - => GetByFileExtension(file?.Extension, defaultMime); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file extension. - /// The MIME content type. - public static string GetByFileExtension(string fileExtension) - => GetByFileExtension(fileExtension, StreamMIME); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file extension. - /// true if returns null if not supported; otherwise, false. - /// The MIME content type. - public static string GetByFileExtension(string fileExtension, bool returnNullIfUnsupported) - => GetByFileExtension(fileExtension, returnNullIfUnsupported ? null : StreamMIME); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file extension. - /// The default MIME content type. - /// The MIME content type. - public static string GetByFileExtension(string fileExtension, string defaultMime) - { - if (string.IsNullOrWhiteSpace(fileExtension)) return null; - if (method == null) - { - method = typeof(WebFormat).GetMethod("GetMime", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(string) }, null); - if (method == null) - method = typeof(WebFormat).GetMethod("GetMime", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null); - if (method == null) - return defaultMime; - } - - var r = method.Invoke(null, new object[] { fileExtension }); - if (r == null) return defaultMime; - try - { - return (string)r; - } - catch (InvalidCastException) - { - } - - return defaultMime; - } - - /// - /// Registers file extension mapping. - /// - /// The mapping source. - /// true if override the existed one; otherwise, false. - /// The count of item added or changed. - public static int RegisterFileExtensionMapping(JsonObjectNode json, bool overrideIfExist = false) - { - var mapping = FileExtensionMapping; - if (json == null || mapping == null) return 0; - var arr = json.TryGetArrayValue("mime"); - if (arr != null) return RegisterFileExtensionMapping(arr, overrideIfExist); - var body = json.TryGetObjectValue("mime"); - if (body != null) json = body; - var i = 0; - foreach (var item in json) - { - if (item.Value?.ValueKind != JsonValueKind.String) continue; - if (!item.Value.TryGetString(out var s)) continue; - if (mapping.Set(item.Key, s, overrideIfExist)) i++; - } - - return i; - } - - /// - /// Registers file extension mapping. - /// - /// The source. - /// true if override the existed one; otherwise, false. - /// The count of item added or changed. - public static int RegisterFileExtensionMapping(JsonArrayNode json, bool overrideIfExist = false) - { - var mapping = FileExtensionMapping; - if (json == null || mapping == null) return 0; - var i = 0; - foreach (var item in json) - { - if (item is not JsonObjectNode ele) continue; - var ext = ele.TryGetStringValue("extension") ?? ele.TryGetStringValue("ext"); - var mime = ele.TryGetStringValue("mime"); - if (mapping.Set(mime, ext, overrideIfExist)) i++; - } - - return i; - } - - /// - /// Registers file extension mapping. - /// - /// The mapping source. - /// true if override the existed one; otherwise, false. - /// The count of item added or changed. - public static int RegisterFileExtensionMapping(IDictionary source, bool overrideIfExist = false) - { - var mapping = FileExtensionMapping; - if (mapping == null || source == null) return 0; - return mapping.Set(source, overrideIfExist); - } - - /// - /// Registers file extension mapping. - /// - /// The file extension part. - /// The MIME content type. - /// true if override the existed one; otherwise, false. - /// true if registers succeeded; otherwise, false. - public static bool RegisterFileExtensionMapping(string fileExtension, string mime, bool overrideIfExist = false) - { - var mapping = FileExtensionMapping; - if (mapping == null || fileExtension == null) return false; - return mapping.Set(fileExtension, mime, overrideIfExist); - } -} diff --git a/Messages/Web/MultipartConstants.cs b/Messages/Web/MultipartConstants.cs deleted file mode 100644 index 9b8530ef..00000000 --- a/Messages/Web/MultipartConstants.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The popular MIME constants of multipart. - /// - public static class Multipart - { - /// - /// The MIME content type of form data. - /// - public const string FormDataMIME = "multipart/form-data"; - - /// - /// The MIME content type of byte ranges. - /// - public const string ByteRanges = "multipart/byteranges"; - - /// - /// The MIME content type of encrypted. - /// - public const string Encrypted = "multipart/encrypted"; - - /// - /// The MIME content type of multilingual. - /// - public const string Multilingual = "multipart/multilingual"; - - /// - /// The MIME content type of signed. - /// - public const string Signed = "multipart/signed"; - - /// - /// The MIME content type of voice message. - /// - public const string VoiceMessage = "multipart/voice-message"; - - /// - /// The MIME content type of mixed replace. - /// - public const string MixedReplace = "multipart/x-mixed-replace"; - } -} diff --git a/Messages/Web/PackagesMimeConstants.cs b/Messages/Web/PackagesMimeConstants.cs deleted file mode 100644 index 2c706369..00000000 --- a/Messages/Web/PackagesMimeConstants.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The popular MIME constants of package and compressed file. - /// - public static class Packages - { - /// - /// The MIME content type of Windows application program package. - /// - public const string Application = "application/x-ms-application"; - - /// - /// The MIME content type of ISO 9660 (disc image). - /// - public const string Iso = "application/x-iso9660-image"; - - /// - /// The MIME content type of Microsoft file required downloading. - /// - public const string DownloadToRun = "application/x-msdownload"; - - /// - /// The MIME content type of Windows App package. - /// - public const string Appx = "application/appx"; - - /// - /// The MIME content type of Windows App bundle package. - /// - public const string AppxBundle = "application/appxbundle"; - - /// - /// The MIME content type of Windows App installer. - /// - public const string AppInstaller = "application/appinstaller"; - - /// - /// The MIME content type of Microsoft Installer. - /// - public const string Msix = "application/msix"; - - /// - /// The MIME content type of Microsoft Installer bundler. - /// - public const string MsixBundle = "application/msixbundle"; - - /// - /// The MIME content type of Android App Package. - /// - public const string Apk = "application/vnd.android.package-archive"; - - /// - /// The MIME content type of object. - /// - public const string O = "application/x-object"; - - /// - /// The MIME content type of object. - /// - public const string Obj = "application/x-tgif"; - - /// - /// The MIME content type of Zip. - /// - public const string Zip = "application/zip"; - - /// - /// The MIME content type of 7z. - /// - public const string SevenZip = "application/x-7z-compressed"; - - /// - /// The MIME content type of cab. - /// - public const string Cab = "application/vnd.ms-cab-compressed"; - - /// - /// The MIME content type of WinRAR. - /// - public const string Rar = "application/x-rar-compressed"; - - /// - /// The MIME content type of Tape Archive. - /// - public const string Tar = "application/tar"; - - /// - /// The MIME content type of GNU Zip. - /// - public const string Gz = "application/gzip"; - - /// - /// The MIME content type of brotli. - /// - public const string Brotli = "application/brotli"; - - /// - /// The MIME content type of Tape Archive on GNU Zip. - /// - public const string Tgz = "application/tar+gzip"; - - /// - /// The MIME content type of z compress file. - /// - public const string Z = "application/x-compress"; - } -} diff --git a/Messages/Web/Registry.cs b/Messages/Web/Registry.cs deleted file mode 100644 index 90318086..00000000 --- a/Messages/Web/Registry.cs +++ /dev/null @@ -1,242 +0,0 @@ -using System; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Security; - -using Trivial.Data; -using Trivial.IO; - -using Reg = Microsoft.Win32.Registry; -using RegistryKey = Microsoft.Win32.RegistryKey; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The Windows Registry access. - /// -#if NET6_0_OR_GREATER -[System.Runtime.Versioning.SupportedOSPlatform("windows")] -#endif - public static class Registry - { - /// - /// Gets the MIME content type by file extension part. - /// - /// The file information. - /// The MIME content type. - public static string GetByFileExtension(FileInfo file) - => GetByFileExtension(file?.Extension, StreamMIME); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file information. - /// The default MIME content type. - /// The MIME content type. - public static string GetByFileExtension(FileInfo file, string defaultMime) - => GetByFileExtension(file?.Extension, defaultMime); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file information. - /// The MIME content type. - public static string GetByFileExtension(LocalFileReferenceInfo file) - => GetByFileExtension(file?.Extension, StreamMIME); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file information. - /// The default MIME content type. - /// The MIME content type. - public static string GetByFileExtension(LocalFileReferenceInfo file, string defaultMime) - => GetByFileExtension(file?.Extension, defaultMime); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file extension. - /// The MIME content type. - public static string GetByFileExtension(string fileExtension) - => GetByFileExtension(fileExtension, StreamMIME); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file extension. - /// true if returns null if not supported; otherwise, false. - /// The MIME content type. - public static string GetByFileExtension(string fileExtension, bool returnNullIfUnsupported) - => GetByFileExtension(fileExtension, returnNullIfUnsupported ? null : StreamMIME); - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file extension. - /// The default MIME content type. - /// The MIME content type. - public static string GetByFileExtension(string fileExtension, string defaultMime) - { - using var regKey = GetRegistryKeyByFileExtension(fileExtension, true); - return RegistryUtility.TryGetStringValue(regKey, "Content Type", defaultMime); - } - - /// - /// Registers file extension mapping. - /// - /// The file information. - /// true if override the existed one; otherwise, false. - /// true if registers succeeded; otherwise, false. - public static bool RegisterFileExtensionMapping(FileInfo file, bool overrideIfExist = false) - => RegisterFileExtensionMapping(file?.Extension, overrideIfExist); - - /// - /// Registers file extension mapping. - /// - /// The file extension to register. - /// true if override the existed one; otherwise, false. - /// true if registers succeeded; otherwise, false. - public static bool RegisterFileExtensionMapping(string fileExtension, bool overrideIfExist = false) - { - if (!overrideIfExist && FileExtensionMapping.ContainsKey(fileExtension)) return false; - var r = GetRegistryKeyByFileExtension(fileExtension, false); - string v; - if (r != null) - { - v = RegistryUtility.TryGetStringValue(r, "Content Type"); - if (string.IsNullOrWhiteSpace(v)) return false; - return FileExtensionMapping.Set(fileExtension, v, overrideIfExist); - } - - if (fileExtension.IndexOf('/') < 1) return false; - v = fileExtension; - using var mime = RegistryUtility.TryOpenSubKey(Reg.ClassesRoot, "MIME\\Database\\Content Type\\" + fileExtension); - fileExtension = RegistryUtility.TryGetStringValue(mime, "Extension"); - return FileExtensionMapping.Set(fileExtension, v, overrideIfExist); - } - - /// - /// Gets the file association information. - /// - /// The file information. - /// The file association information; or null, if not found or failure. - public static FileAssociationInfo GetFileAssociationInfo(FileInfo file) - => GetFileAssociationInfo(file?.Extension); - - /// - /// Gets the file association information. - /// - /// The file information. - /// The file association information; or null, if not found or failure. - public static FileAssociationInfo GetFileAssociationInfo(LocalFileReferenceInfo file) - => GetFileAssociationInfo(file?.Extension); - - /// - /// Gets the file association information. - /// - /// The file extension part. - /// The file association information; or null, if not found or failure. - public static FileAssociationInfo GetFileAssociationInfo(string fileExtension) - { - fileExtension = fileExtension?.Trim()?.ToLowerInvariant(); - if (string.IsNullOrEmpty(fileExtension)) return null; - var info = new FileAssociationInfo - { - FileExtension = fileExtension - }; - using var ext = RegistryUtility.TryOpenSubKey(Reg.ClassesRoot, fileExtension); - string h; - if (ext == null) - { - if (fileExtension.IndexOf('/') < 1) return info; - using var mime = RegistryUtility.TryOpenSubKey(Reg.ClassesRoot, "MIME\\Database\\Content Type\\" + fileExtension); - if (mime == null) return info; - info.ContentType = fileExtension; - fileExtension = RegistryUtility.TryGetStringValue(mime, "Extension"); - info.FileExtension = fileExtension; - using var ext2 = RegistryUtility.TryOpenSubKey(Reg.ClassesRoot, fileExtension); - if (ext2 == null) return info; - h = RegistryUtility.TryGetStringValue(ext2, null); - } - else - { - info.ContentType = RegistryUtility.TryGetStringValue(ext, "Content Type"); - h = RegistryUtility.TryGetStringValue(ext, null); - } - - if (string.IsNullOrEmpty(h)) - { - using var ext2 = RegistryUtility.TryOpenSubKey(ext, "OpenWithProgids"); - if (ext2 == null) return info; - h = RegistryUtility.TryGetValueNames(ext2).FirstOrDefault(ele => !string.IsNullOrWhiteSpace(ele)); - if (string.IsNullOrEmpty(h)) return info; - } - - using var assoc = RegistryUtility.TryOpenSubKey(Reg.ClassesRoot, h); - if (assoc == null) return info; - info.Name = RegistryUtility.TryGetStringValue(assoc, null) ?? RegistryUtility.TryGetStringValue(assoc, "FriendlyTypeName"); - using var icon = RegistryUtility.TryOpenSubKey(assoc, "DefaultIcon"); - if (icon != null) info.Icon = RegistryUtility.TryGetStringValue(icon, null); - using var shell = RegistryUtility.TryOpenSubKey(assoc, "shell"); - if (shell == null) return info; - var defaultCommand = RegistryUtility.TryGetStringValue(shell, null); - var commands = RegistryUtility.TryOpenSubKeys(shell)?.ToList(); - if (commands.Count < 1) return info; - foreach (var command in commands) - { - var name = command.Name; - var pos = name?.LastIndexOf('\\') ?? -1; - if (pos >= 0) name = name.Substring(pos + 1); - var cmd = new FileOpenCommandInfo - { - Key = name, - Name = RegistryUtility.TryGetStringValue(command, null) - }; - var exe = RegistryUtility.TryOpenSubKey(command, "command"); - if (exe == null) continue; - var c = RegistryUtility.TryGetStringValue(exe, null); - if (string.IsNullOrWhiteSpace(c)) continue; - cmd.Command = c; - info.Commands.Add(cmd); - } - - if (!string.IsNullOrWhiteSpace(defaultCommand)) - { - info.DefaultCommand = info.Commands.FirstOrDefault(ele => defaultCommand.Equals(ele.Key, StringComparison.OrdinalIgnoreCase)); - } - - if (info.DefaultCommand == null) info.DefaultCommand = info.Commands.FirstOrDefault(); - return info; - } - - /// - /// Gets the MIME content type by file extension part. - /// - /// The file extension part. - /// true if throw an argument exception if the file extension is null or empty; otherwise, false. - /// The registry key. - /// fileExtension was null. - /// fileExtension was empty. - private static RegistryKey GetRegistryKeyByFileExtension(string fileExtension, bool throwExceptionForNullOrEmpty) - { - if (fileExtension == null && throwExceptionForNullOrEmpty) throw new ArgumentNullException(nameof(fileExtension), "fileExtension should not be null."); - fileExtension = fileExtension.Trim().ToLowerInvariant(); - if (fileExtension.Length < 1) - { - if (throwExceptionForNullOrEmpty) - throw new ArgumentException("fileExtension should not be empty.", nameof(fileExtension)); - return null; - } - - var r = RegistryUtility.TryOpenSubKey(Reg.ClassesRoot, fileExtension); - return r; - } - } -} diff --git a/Messages/Web/TextMimeConstants.cs b/Messages/Web/TextMimeConstants.cs deleted file mode 100644 index 5bebf194..00000000 --- a/Messages/Web/TextMimeConstants.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The popular MIME constants of text and fonts. - /// - public static class Text - { - /// - /// The MIME content type of plain text. - /// - public const string Plain = "text/plain"; - - /// - /// The MIME content type of rich text. - /// - public const string Rtf = "text/richtext"; - - /// - /// The MIME content type of comma-separated values. - /// - public const string Csv = "text/csv"; - - /// - /// The MIME content type of tab-separated values. - /// - public const string Tsv = "text/tsv"; - - /// - /// The MIME content type of vCard (Versitcard). - /// - public const string VCard = "text/x-vcard"; - - /// - /// The MIME content type of diff and patch. - /// - public const string Diff = "text/x-diff"; - - /// - /// The MIME content type of markdown. - /// - public const string Markdown = "text/markdown"; - - /// - /// The MIME content type of math markup. - /// - public const string Math = "text/mathml"; - - /// - /// The MIME content type of Standard Generalized Markup Language. - /// - public const string Sgml = "text/sgml"; - - /// - /// The MIME content type of ink markup. - /// - public const string Ink = "application/inkml+xml"; - - /// - /// The MIME content type of iCalendar. - /// - public const string Calendar = "text/calendar"; - - /// - /// The MIME content type of Microsoft Embedded Open Type. - /// - public const string Eot = "application/vnd.ms-fontobject"; - - /// - /// The MIME content type of TrueType Font. - /// - public const string TrueType = "font/ttf"; - - /// - /// The MIME content type of OpenType Layout Font. - /// - public const string OpenType = "font/otf"; - - /// - /// The MIME content type of Web Open Font Format. - /// - public const string Woff = "font/woff"; - - /// - /// The MIME content type of Web Open Font Format 2. - /// - public const string Woff2 = "font/woff2"; - } -} diff --git a/Messages/Web/VideoMimeConstants.cs b/Messages/Web/VideoMimeConstants.cs deleted file mode 100644 index 62bd990c..00000000 --- a/Messages/Web/VideoMimeConstants.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The popular MIME constants of video. - /// - public static class Videos - { - /// - /// The MIME content type of Audio Video Interleaved. - /// - public const string Avi = "video/x-msvideo"; - - /// - /// The MIME content type of H.264 (Advanced Video Coding). - /// - public const string Avc = "video/h264"; - - /// - /// The MIME content type of H.265 (High Efficiency Video Coding). - /// - public const string Hevc = "video/h265"; - - /// - /// The MIME content type of H.266 (Versatile Video Coding). - /// - public const string Vvc = "video/h266"; - - /// - /// The MIME content type of H.267. - /// - public const string H267 = "video/h267"; - - /// - /// The MIME content type of H.268. - /// - public const string H268 = "video/h268"; - - /// - /// The MIME content type of AV1, a video format by Alliance of Open Media. - /// - public const string Av1 = "video/av1"; - - /// - /// The MIME content type of AV2, a video format by Alliance of Open Media. - /// - public const string Av2 = "video/av2"; - - /// - /// The MIME content type of AV3, a video format by Alliance of Open Media. - /// - public const string Av3 = "video/av3"; - - /// - /// The MIME content type of AVS3. - /// - public const string Avs3 = "video/avs3"; - - /// - /// The MIME content type of AVS4. - /// - public const string Avs4 = "video/avs4"; - - /// - /// The MIME content type of JPEG Video. - /// - public const string Jpgv = "video/jpeg"; - - /// - /// The MIME content type of MPEG-4. - /// - public const string Mp4 = "video/mp4"; - - /// - /// The MIME content type of Moving Picture Experts Group. - /// - public const string Mpeg = "video/mpeg"; - - /// - /// The MIME content type of QuickTime (mov). - /// - public const string QuickTime = "video/quicktime"; - - /// - /// The MIME content type of WebM (by Google). - /// - public const string Webm = "video/webm"; - - /// - /// The MIME content type of Matroska. - /// - public const string Matroska = "video/x-matroska"; - - /// - /// The MIME content type of Windows Media Video. - /// - public const string Wmv = "video/x-ms-wm"; - - /// - /// The MIME content type of Advanced Streaming Format. - /// - public const string Asf = "video/x-ms-asf"; - - /// - /// The MIME content type of Windows Media Video. - /// - public const string Wmx = "video/x-ms-wmx"; - } -} diff --git a/Messages/Web/WebMimeConstants.cs b/Messages/Web/WebMimeConstants.cs deleted file mode 100644 index 844e3147..00000000 --- a/Messages/Web/WebMimeConstants.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; - -namespace Trivial.Web; - -/// -/// The MIME constants. -/// -public static partial class MimeConstants -{ - /// - /// The popular MIME constants of web content. - /// - public static class Web - { - /// - /// The MIME content type of Cascading Style Sheets. - /// - public const string Css = "text/css"; - - /// - /// The MIME content type of Syntactically Awesome Style Sheets. - /// - public const string Sass = "text/x-sass"; - - /// - /// The MIME content type of Sassy Cascaded Style Sheets. - /// - public const string Scss = "text/x-scss"; - - /// - /// The MIME content type of Leaner CSS. - /// - public const string Less = "text/x-less"; - - /// - /// The MIME content type of Document Type Definition. - /// - public const string Dtd = "application/xml-dtd"; - - /// - /// The MIME content type of ECMA Script. - /// - public const string Ecma = "application/ecmascript"; - - /// - /// The MIME content type of JavaScript. - /// - public const string Js = "application/javascript"; - - /// - /// The MIME content type of VB Script. - /// - public const string Vbs = "text/vbscript"; - - /// - /// The MIME content type of VB Script. - /// - public const string Dart = "application/vnd.dart"; - - /// - /// The MIME content type of web assembly. - /// - public const string Wasm = "application/wasm"; - - /// - /// The MIME content type of HTML (Hypertext Markup Language). - /// - public const string Html = "text/html"; - - /// - /// The MIME content type of JSON (JavaScript Object Notation). - /// - public const string Json = "application/json"; - - /// - /// The MIME content type of JSON lines. - /// - public const string Jsonl = "application/jsonl"; - - /// - /// The MIME content type of YAML (YAML Ain't Markup Language). - /// - public const string Yaml = "application/x-yaml"; - - /// - /// The MIME content type of Extensible Markup Language. - /// - public const string Xml = "application/xml"; - - /// - /// The MIME content type of Uniform Resource Identifier list. - /// - public const string Uri = "text/uri-list"; - } -} diff --git a/Messages/Data/AttachmentLinkItem.cs b/Mime/Data/AttachmentLinkItem.cs similarity index 100% rename from Messages/Data/AttachmentLinkItem.cs rename to Mime/Data/AttachmentLinkItem.cs diff --git a/Mime/Mime.csproj b/Mime/Mime.csproj index bbbb2827..ab0a7787 100644 --- a/Mime/Mime.csproj +++ b/Mime/Mime.csproj @@ -1,60 +1,28 @@  + + net8.0;net6.0;net48;net462;net461 Trivial.Mime Trivial Trivial.Mime - Trivial - Kingcean Tuan - Nanchang Jinchen Software Co., Ltd. - 8.0.0 - 8.0.0.0 - 8.0.0.0 The MIME constants and the barcode generators. - Copyright (c) 2018 Kingcean Tuan. - MIT https://github.com/nuscien/trivial/wiki/core true true snupkg - https://github.com/nuscien/trivial - git - true mime.png https://github.com/nuscien/trivial/raw/master/Materials/logo.png mime files barcode - README.md - 12.0 - True - ..\Trivial.snk - - - - ..\bin\Debug\ - ..\bin\$(Configuration)\$(TargetFramework)\Trivial.Mime.xml - - ..\bin\Release\ + + ..\bin\$(Configuration)\ ..\bin\$(Configuration)\$(TargetFramework)\Trivial.Mime.xml - - NETOLDVER - - - - - - - - - - - - diff --git a/Mime/Text/ChatMessageModification.cs b/Mime/Text/ChatMessageModification.cs new file mode 100644 index 00000000..cfcfdd9f --- /dev/null +++ b/Mime/Text/ChatMessageModification.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Trivial.Text; + +/// +/// The modification kinds of the message. +/// +public enum ChatMessageModificationKinds : byte +{ + /// + /// The message is neve modified. + /// + Original = 0, + + /// + /// The streaming message which means the message is transferring by continious updating. + /// + Streaming = 1, + + /// + /// The message has been modified by sender. + /// + Modified = 2, + + /// + /// The message has been modified and is open to update by others. + /// + Collaborative = 3, + + /// + /// The message has been removed by sender. + /// + Removed = 5, + + /// + /// The message is banned by system. + /// + Ban = 9, + + /// + /// Others. + /// + Others = 15 +} diff --git a/Trivial.sln b/Trivial.sln index 555fe877..cbfe9281 100644 --- a/Trivial.sln +++ b/Trivial.sln @@ -20,8 +20,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "UnitTest\UnitTe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mime", "Mime\Mime.csproj", "{8C8EAF77-6182-4F04-87AA-1700FC00100D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Messages", "Messages\Messages.csproj", "{424341F2-7CE8-4BF7-B788-0B2C52D31D94}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,10 +38,6 @@ Global {8C8EAF77-6182-4F04-87AA-1700FC00100D}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C8EAF77-6182-4F04-87AA-1700FC00100D}.Release|Any CPU.ActiveCfg = Release|Any CPU {8C8EAF77-6182-4F04-87AA-1700FC00100D}.Release|Any CPU.Build.0 = Release|Any CPU - {424341F2-7CE8-4BF7-B788-0B2C52D31D94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {424341F2-7CE8-4BF7-B788-0B2C52D31D94}.Debug|Any CPU.Build.0 = Debug|Any CPU - {424341F2-7CE8-4BF7-B788-0B2C52D31D94}.Release|Any CPU.ActiveCfg = Release|Any CPU - {424341F2-7CE8-4BF7-B788-0B2C52D31D94}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE