ForcomeBot 2.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:#fef2f2;--color-red-100:#ffe2e2;--color-red-200:#ffcaca;--color-red-300:#ffa3a3;--color-red-500:#fb2c36;--color-red-600:#e40014;--color-red-700:#bf000f;--color-red-800:#9f0712;--color-yellow-50:#fefce8;--color-yellow-100:#fef9c2;--color-yellow-200:#fff085;--color-yellow-500:#edb200;--color-yellow-600:#cd8900;--color-yellow-800:#874b00;--color-green-50:#f0fdf4;--color-green-100:#dcfce7;--color-green-200:#b9f8cf;--color-green-500:#00c758;--color-green-600:#00a544;--color-green-700:#008138;--color-green-800:#016630;--color-blue-50:#eff6ff;--color-blue-100:#dbeafe;--color-blue-200:#bedbff;--color-blue-300:#90c5ff;--color-blue-400:#54a2ff;--color-blue-500:#3080ff;--color-blue-600:#155dfc;--color-blue-700:#1447e6;--color-blue-800:#193cb8;--color-purple-100:#f3e8ff;--color-gray-50:#f9fafb;--color-gray-100:#f3f4f6;--color-gray-200:#e5e7eb;--color-gray-300:#d1d5dc;--color-gray-400:#99a1af;--color-gray-500:#6a7282;--color-gray-600:#4a5565;--color-gray-700:#364153;--color-gray-800:#1e2939;--color-gray-900:#101828;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height:calc(1.5/1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--radius-md:.375rem;--radius-lg:.5rem;--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-ping:ping 1s cubic-bezier(0,0,.2,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}@supports (color:lab(0% 0 0)){:root,:host{--color-red-50:lab(96.5005% 4.18511 1.52329);--color-red-100:lab(92.243% 10.2865 3.83865);--color-red-200:lab(86.017% 19.8815 7.75869);--color-red-300:lab(76.5514% 36.4219 15.5335);--color-red-500:lab(55.4814% 75.0732 48.8528);--color-red-600:lab(48.4493% 77.4328 61.5452);--color-red-700:lab(40.4273% 67.2623 53.7441);--color-red-800:lab(33.7174% 55.8993 41.0293);--color-yellow-50:lab(98.6846% -1.79058 9.77662);--color-yellow-100:lab(97.3564% -4.51407 27.344);--color-yellow-200:lab(94.3433% -5.00426 52.9663);--color-yellow-500:lab(76.3898% 14.5258 98.4589);--color-yellow-600:lab(62.7799% 22.4198 86.1544);--color-yellow-800:lab(38.7484% 23.5833 51.4916);--color-green-50:lab(98.1563% -5.60117 2.75913);--color-green-100:lab(96.186% -13.8464 6.52362);--color-green-200:lab(92.4222% -26.4702 12.9427);--color-green-500:lab(70.5521% -66.5147 45.8072);--color-green-600:lab(59.0978% -58.6621 41.2579);--color-green-700:lab(47.0329% -47.0239 31.4788);--color-green-800:lab(37.4616% -36.7971 22.9692);--color-blue-50:lab(96.492% -1.14647 -5.11479);--color-blue-100:lab(92.0301% -2.24757 -11.6453);--color-blue-200:lab(86.15% -4.04379 -21.0797);--color-blue-300:lab(77.5052% -6.4629 -36.42);--color-blue-400:lab(65.0361% -1.42062 -56.9803);--color-blue-500:lab(54.1736% 13.3368 -74.6839);--color-blue-600:lab(44.0605% 29.0279 -86.0352);--color-blue-700:lab(36.9089% 35.0961 -85.6872);--color-blue-800:lab(30.2514% 27.7854 -70.2699);--color-purple-100:lab(93.3333% 6.9744 -9.83434);--color-gray-50:lab(98.2596% -.247031 -.706708);--color-gray-100:lab(96.1596% -.082314 -1.13575);--color-gray-200:lab(91.6229% -.159085 -2.26791);--color-gray-300:lab(85.1236% -.612259 -3.7138);--color-gray-400:lab(65.9269% -.832707 -8.17474);--color-gray-500:lab(47.7841% -.393212 -10.0268);--color-gray-600:lab(35.6337% -1.58697 -10.8425);--color-gray-700:lab(27.1134% -.956401 -12.3224);--color-gray-800:lab(16.1051% -1.18239 -11.7533);--color-gray-900:lab(8.11897% .811279 -12.254)}}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.-top-1{top:calc(var(--spacing)*-1)}.top-4{top:calc(var(--spacing)*4)}.-right-1{right:calc(var(--spacing)*-1)}.right-4{right:calc(var(--spacing)*4)}.z-50{z-index:50}.col-span-1{grid-column:span 1/span 1}.mx-auto{margin-inline:auto}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mr-2{margin-right:calc(var(--spacing)*2)}.mr-3{margin-right:calc(var(--spacing)*3)}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.-ml-1{margin-left:calc(var(--spacing)*-1)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-4{margin-left:calc(var(--spacing)*4)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.h-2{height:calc(var(--spacing)*2)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-16{height:calc(var(--spacing)*16)}.h-64{height:calc(var(--spacing)*64)}.h-full{height:100%}.max-h-32{max-height:calc(var(--spacing)*32)}.max-h-80{max-height:calc(var(--spacing)*80)}.max-h-\[600px\]{max-height:600px}.min-h-\[38px\]{min-height:38px}.min-h-\[60px\]{min-height:60px}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-2{width:calc(var(--spacing)*2)}.w-3{width:calc(var(--spacing)*3)}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-10{width:calc(var(--spacing)*10)}.w-12{width:calc(var(--spacing)*12)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-28{width:calc(var(--spacing)*28)}.w-full{width:100%}.max-w-7xl{max-width:var(--container-7xl)}.max-w-\[200px\]{max-width:200px}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-sm{max-width:var(--container-sm)}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-full{min-width:100%}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-4{--tw-translate-x:calc(var(--spacing)*4);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-full{--tw-translate-x:100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.rotate-90{rotate:90deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-ping{animation:var(--animate-ping)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-1>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*1)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-2>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*2)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*3)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-4>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*4)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-8>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing)*8)*var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-gray-200>:not(:last-child)){border-color:var(--color-gray-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-b-md{border-bottom-right-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-blue-200{border-color:var(--color-blue-200)}.border-blue-300{border-color:var(--color-blue-300)}.border-blue-500{border-color:var(--color-blue-500)}.border-blue-600{border-color:var(--color-blue-600)}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-green-200{border-color:var(--color-green-200)}.border-red-200{border-color:var(--color-red-200)}.border-red-300{border-color:var(--color-red-300)}.border-transparent{border-color:#0000}.border-yellow-200{border-color:var(--color-yellow-200)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-600{background-color:var(--color-blue-600)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-200{background-color:var(--color-gray-200)}.bg-gray-400{background-color:var(--color-gray-400)}.bg-green-50{background-color:var(--color-green-50)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-500{background-color:var(--color-green-500)}.bg-purple-100{background-color:var(--color-purple-100)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-600{background-color:var(--color-red-600)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-yellow-50{background-color:var(--color-yellow-50)}.bg-yellow-100{background-color:var(--color-yellow-100)}.bg-yellow-500{background-color:var(--color-yellow-500)}.object-cover{object-fit:cover}.p-1{padding:calc(var(--spacing)*1)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-12{padding-block:calc(var(--spacing)*12)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pb-3{padding-bottom:calc(var(--spacing)*3)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-blue-800{color:var(--color-blue-800)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-green-600{color:var(--color-green-600)}.text-green-700{color:var(--color-green-700)}.text-green-800{color:var(--color-green-800)}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-red-700{color:var(--color-red-700)}.text-red-800{color:var(--color-red-800)}.text-white{color:var(--color-white)}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-800{color:var(--color-yellow-800)}.uppercase{text-transform:uppercase}.opacity-0{opacity:0}.opacity-25{opacity:.25}.opacity-60{opacity:.6}.opacity-75{opacity:.75}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-0{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(0px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}@media (hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:border-blue-400:hover{border-color:var(--color-blue-400)}.hover\:border-gray-300:hover{border-color:var(--color-gray-300)}.hover\:bg-blue-700:hover{background-color:var(--color-blue-700)}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-200:hover{background-color:var(--color-gray-200)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:text-blue-700:hover{color:var(--color-blue-700)}.hover\:text-blue-800:hover{color:var(--color-blue-800)}.hover\:text-gray-500:hover{color:var(--color-gray-500)}.hover\:text-gray-600:hover{color:var(--color-gray-600)}.hover\:text-gray-700:hover{color:var(--color-gray-700)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}.hover\:text-red-700:hover{color:var(--color-red-700)}.hover\:text-red-800:hover{color:var(--color-red-800)}}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-500:focus{--tw-ring-color:var(--color-blue-500)}.focus\:ring-gray-500:focus{--tw-ring-color:var(--color-gray-500)}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-100:disabled{background-color:var(--color-gray-100)}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:px-6{padding-inline:calc(var(--spacing)*6)}.sm\:text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}}@media (min-width:48rem){.md\:flex{display:flex}.md\:hidden{display:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:px-8{padding-inline:calc(var(--spacing)*8)}}}:root{font-family:system-ui,Avenir,Helvetica,Arial,sans-serif;font-weight:400;line-height:1.5}body{min-width:320px;min-height:100vh;margin:0}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}
src/static/index.html ADDED
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/app/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>FORCOME 康康</title>
8
+ <script type="module" crossorigin src="/app/assets/index-B4i68B5_.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/app/assets/index-BPXisDkw.css">
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ </body>
14
+ </html>
src/static/vite.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
src/utils/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ # Utils layer
2
+ """Utility modules for text processing and XML parsing."""
3
+
4
+ from .text_processor import TextProcessor
5
+ from .xml_parser import XMLParser, QuoteMessageResult, PatMessageResult, VoiceInfo
6
+
7
+ __all__ = [
8
+ 'TextProcessor',
9
+ 'XMLParser',
10
+ 'QuoteMessageResult',
11
+ 'PatMessageResult',
12
+ 'VoiceInfo',
13
+ ]
@@ -0,0 +1,166 @@
1
+ """文本处理器 - 处理换行符和emoji编解码
2
+
3
+ 保持与原有业务逻辑完全一致:
4
+ - 千寻框架 sendText API 的换行符处理:
5
+ - \n 会显示为两行(有空行/段落间距)
6
+ - \r 会显示为单行换行(无空行)
7
+ - Emoji 使用 \\uXXXX 格式编码(含 surrogate pair)
8
+ """
9
+ import re
10
+ import logging
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class TextProcessor:
16
+ """文本处理器 - 保持原有逻辑"""
17
+
18
+ @staticmethod
19
+ def encode_for_qianxun(text: str) -> str:
20
+ """编码文本用于发送到千寻框架
21
+
22
+ 换行符处理(保持原逻辑):
23
+ - \n 会显示为两行(有空行)
24
+ - \r 会显示为单行换行(无空行)
25
+
26
+ 处理步骤:
27
+ 1. 先统一所有换行符为 \n
28
+ 2. 临时标记双换行(空行)
29
+ 3. 单换行 → \r
30
+ 4. 双换行(空行)→ \n
31
+ 5. emoji编码
32
+
33
+ Args:
34
+ text: 原始文本
35
+
36
+ Returns:
37
+ 编码后的文本,可直接发送到千寻框架
38
+ """
39
+ # 先统一为 \n
40
+ text = text.replace('\r\n', '\n').replace('\r', '\n')
41
+ # 临时标记双换行(空行)
42
+ text = text.replace('\n\n', '\x00')
43
+ # 单换行 → \r
44
+ text = text.replace('\n', '\r')
45
+ # 双换行(空行)→ \n(千寻会显示为两行)
46
+ text = text.replace('\x00', '\n')
47
+ # emoji编码
48
+ text = TextProcessor.encode_emoji(text)
49
+ return text
50
+
51
+ @staticmethod
52
+ def encode_emoji(text: str) -> str:
53
+ """把emoji等非BMP字符转换为\\uXXXX格式
54
+
55
+ 千寻框架需要 \\uXXXX 格式的 emoji 转义
56
+ 非 BMP 字符(emoji 等)需要转换为 surrogate pair
57
+
58
+ Args:
59
+ text: 原始文本
60
+
61
+ Returns:
62
+ 编码后的文本
63
+ """
64
+ result = []
65
+ for char in text:
66
+ code = ord(char)
67
+ # 非 BMP 字符(emoji 等)需要转换为 surrogate pair
68
+ if code > 0xFFFF:
69
+ code -= 0x10000
70
+ high = 0xD800 + (code >> 10)
71
+ low = 0xDC00 + (code & 0x3FF)
72
+ result.append(f'\\u{high:04X}\\u{low:04X}')
73
+ else:
74
+ result.append(char)
75
+ return ''.join(result)
76
+
77
+ @staticmethod
78
+ def decode_emoji(text: str) -> str:
79
+ """解码\\uXXXX格式为真正的字符
80
+
81
+ 处理两种情况:
82
+ 1. surrogate pair 格式的 emoji: \\uD83D\\uDE00 -> 😀
83
+ 2. 普通 Unicode 转义: \\u4F60 -> 你
84
+
85
+ Args:
86
+ text: 包含\\uXXXX格式的文本
87
+
88
+ Returns:
89
+ 解码后的文本
90
+ """
91
+ if not text or '\\u' not in text:
92
+ return text
93
+
94
+ try:
95
+ # 先处理 surrogate pair 格式的 emoji
96
+ def replace_surrogate(match):
97
+ try:
98
+ high = int(match.group(1), 16)
99
+ low = int(match.group(2), 16)
100
+ # 验证是否是有效的 surrogate pair
101
+ if 0xD800 <= high <= 0xDBFF and 0xDC00 <= low <= 0xDFFF:
102
+ # 转换为真正的 Unicode 字符
103
+ code_point = 0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00)
104
+ return chr(code_point)
105
+ return match.group(0)
106
+ except:
107
+ return match.group(0)
108
+
109
+ # 匹配 \uXXXX\uXXXX 格式的 surrogate pair
110
+ surrogate_pattern = r'\\u([dD][89aAbB][0-9a-fA-F]{2})\\u([dD][cCdDeEfF][0-9a-fA-F]{2})'
111
+ result = re.sub(surrogate_pattern, replace_surrogate, text)
112
+
113
+ # 再处理剩余的普通 \uXXXX 格式(非 surrogate)
114
+ def replace_unicode(match):
115
+ try:
116
+ code = int(match.group(1), 16)
117
+ # 跳过 surrogate 范围(已经处理过了)
118
+ if 0xD800 <= code <= 0xDFFF:
119
+ return match.group(0)
120
+ return chr(code)
121
+ except:
122
+ return match.group(0)
123
+
124
+ unicode_pattern = r'\\u([0-9a-fA-F]{4})'
125
+ result = re.sub(unicode_pattern, replace_unicode, result)
126
+
127
+ return result
128
+ except Exception as e:
129
+ logger.debug(f"解码 Unicode 转义失败: {e}")
130
+ return text
131
+
132
+ @staticmethod
133
+ def config_to_text(text: str) -> str:
134
+ """配置文件中的\\n字符串转换为真正的换行符
135
+
136
+ 配置文件中存储的是字面量 \\n(两个字符),
137
+ 需要转换为真正的换行符 \n(一个字符)
138
+
139
+ Args:
140
+ text: 配置文件中的文本
141
+
142
+ Returns:
143
+ 转换后的文本
144
+ """
145
+ return text.replace('\\n', '\n')
146
+
147
+ @staticmethod
148
+ def text_to_config(text: str) -> str:
149
+ """文本中的换行符转换为配置文件格式
150
+
151
+ 将真正的换行符转换为字面量 \\n,用于保存到配置文件
152
+ 保留空行,只去除每行末尾空格
153
+
154
+ Args:
155
+ text: 原始文本
156
+
157
+ Returns:
158
+ 配置文件格式的文本
159
+ """
160
+ # 统一换行符
161
+ text = text.replace('\r\n', '\n').replace('\r', '')
162
+ if '\n' in text:
163
+ # 保留空行,只去除每行末尾空格
164
+ lines = [line.rstrip() for line in text.split('\n')]
165
+ return '\\n'.join(lines)
166
+ return text
@@ -0,0 +1,215 @@
1
+ """XML解析工具 - 解析微信消息中的XML内容
2
+
3
+ 支持解析:
4
+ - 引用消息(检查是否引用机器人)
5
+ - 拍一拍消息
6
+ - 语音消息
7
+ """
8
+ import re
9
+ import logging
10
+ import xml.etree.ElementTree as ET
11
+ from dataclasses import dataclass
12
+ from typing import Optional
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @dataclass
18
+ class QuoteMessageResult:
19
+ """引用消息解析结果"""
20
+ user_msg: str # 用户发送的新消息
21
+ quoted_text: str # 被引用的消息内容
22
+ sender_name: str # 被引用消息发送者的昵称
23
+ quoted_image_path: Optional[str] = None # 引用的图片路径(如果引用的是图片消息)
24
+
25
+
26
+ @dataclass
27
+ class PatMessageResult:
28
+ """拍一拍消息解析结果"""
29
+ from_user: str # 发起拍一拍的用户
30
+ patted_user: str # 被拍的用户
31
+ is_pat_me: bool # 是否拍的是机器人
32
+
33
+
34
+ @dataclass
35
+ class VoiceInfo:
36
+ """语音消息信息"""
37
+ voicelength: int # 语音长度(毫秒)
38
+
39
+
40
+ class XMLParser:
41
+ """XML解析工具"""
42
+
43
+ @staticmethod
44
+ def parse_quote_message(xml_content: str, robot_wxid: str) -> Optional[QuoteMessageResult]:
45
+ """解析引用消息,如果引用的是机器人的消息则返回解析结果
46
+
47
+ Args:
48
+ xml_content: 引用消息的XML内容
49
+ robot_wxid: 机器人wxid,用于检查是否引用机器人
50
+
51
+ Returns:
52
+ QuoteMessageResult 如果引用的是机器人消息,否则返回 None
53
+ """
54
+ try:
55
+ root = ET.fromstring(xml_content)
56
+
57
+ # 获取引用信息
58
+ refermsg = root.find('.//refermsg')
59
+ if refermsg is None:
60
+ return None
61
+
62
+ # 检查被引用消息的发送者是否是机器人
63
+ chatusr = refermsg.find('chatusr')
64
+ if chatusr is None or chatusr.text != robot_wxid:
65
+ return None
66
+
67
+ # 获取用户发送的新消息(title标签)
68
+ title = root.find('.//title')
69
+ user_msg = title.text.strip() if title is not None and title.text else ""
70
+
71
+ # 获取被引用的消息内容
72
+ quote_content = refermsg.find('content')
73
+ quoted_text = quote_content.text.strip() if quote_content is not None and quote_content.text else ""
74
+
75
+ # 获取被引用消息发送者的昵称
76
+ displayname = refermsg.find('displayname')
77
+ sender_name = displayname.text.strip() if displayname is not None and displayname.text else "未知"
78
+
79
+ if user_msg:
80
+ return QuoteMessageResult(
81
+ user_msg=user_msg,
82
+ quoted_text=quoted_text,
83
+ sender_name=sender_name
84
+ )
85
+
86
+ return None
87
+ except Exception as e:
88
+ logger.error(f"解析引用消息XML失败: {e}")
89
+ return None
90
+
91
+ @staticmethod
92
+ def parse_quote_message_any(xml_content: str) -> Optional[QuoteMessageResult]:
93
+ """解析引用消息(不检查被引用者)
94
+
95
+ Args:
96
+ xml_content: 引用消息的XML内容
97
+
98
+ Returns:
99
+ QuoteMessageResult 包含用户消息、引用内容、发送者昵称和可能的图片路径
100
+ """
101
+ try:
102
+ root = ET.fromstring(xml_content)
103
+
104
+ # 获取用户发送的新消息
105
+ title = root.find('.//title')
106
+ user_msg = title.text.strip() if title is not None and title.text else ""
107
+
108
+ # 获取被引用的消息内容和发送者昵称
109
+ refermsg = root.find('.//refermsg')
110
+ quoted_text = ""
111
+ sender_name = "未知"
112
+ quoted_image_path = None
113
+
114
+ if refermsg is not None:
115
+ quote_content = refermsg.find('content')
116
+ quoted_text = quote_content.text.strip() if quote_content is not None and quote_content.text else ""
117
+ displayname = refermsg.find('displayname')
118
+ sender_name = displayname.text.strip() if displayname is not None and displayname.text else "未知"
119
+
120
+ # 检查引用的是否是图片消息
121
+ # 图片消息的 type 为 3,content 格式为 [pic=路径,isDecrypt=x]
122
+ msg_type = refermsg.find('type')
123
+ if msg_type is not None and msg_type.text == '3':
124
+ # 解析图片路径
125
+ match = re.search(r'\[pic=([^,\]]+)', quoted_text)
126
+ if match:
127
+ quoted_image_path = match.group(1)
128
+ quoted_text = "[图片]" # 替换为友好文本
129
+
130
+ if user_msg:
131
+ return QuoteMessageResult(
132
+ user_msg=user_msg,
133
+ quoted_text=quoted_text,
134
+ sender_name=sender_name,
135
+ quoted_image_path=quoted_image_path
136
+ )
137
+ return None
138
+ except Exception as e:
139
+ logger.error(f"解析引用消息XML失败: {e}")
140
+ return None
141
+
142
+ @staticmethod
143
+ def parse_pat_message(xml_content: str, robot_wxid: str) -> Optional[PatMessageResult]:
144
+ """解析拍一拍消息(XML格式,私聊或部分群聊)
145
+
146
+ Args:
147
+ xml_content: 拍一拍消息的XML内容
148
+ robot_wxid: 机器人wxid,用于判断是否拍的是机器人
149
+
150
+ Returns:
151
+ PatMessageResult 解析结果
152
+ """
153
+ try:
154
+ root = ET.fromstring(xml_content)
155
+
156
+ pat = root.find('pat')
157
+ if pat is None:
158
+ return None
159
+
160
+ from_user = pat.findtext('fromusername', '')
161
+ patted_user = pat.findtext('pattedusername', '')
162
+
163
+ return PatMessageResult(
164
+ from_user=from_user,
165
+ patted_user=patted_user,
166
+ is_pat_me=patted_user == robot_wxid
167
+ )
168
+ except Exception as e:
169
+ logger.error(f"解析拍一拍XML失败: {e}")
170
+ return None
171
+
172
+ @staticmethod
173
+ def parse_pat_text(text: str, robot_wxid: str) -> Optional[PatMessageResult]:
174
+ """解析文本格式的拍一拍消息(群聊)
175
+
176
+ 群聊拍一拍文本格式: "xxx" 拍了拍 "yyy"
177
+ 由于文本中没有wxid,无法精确判断是否拍的是机器人,
178
+ 假设收到的拍一拍消息都是拍机器人的
179
+
180
+ Args:
181
+ text: 拍一拍消息文本
182
+ robot_wxid: 机器人wxid(用于判断,但文本中没有wxid)
183
+
184
+ Returns:
185
+ PatMessageResult 解析结果
186
+ """
187
+ if "拍了拍" in text:
188
+ return PatMessageResult(
189
+ from_user="", # 群聊文本格式无法获取wxid
190
+ patted_user="",
191
+ is_pat_me=True # 假设收到的拍一拍都是拍机器人的
192
+ )
193
+ return None
194
+
195
+ @staticmethod
196
+ def parse_voice_info(xml_content: str) -> Optional[VoiceInfo]:
197
+ """解析语音消息
198
+
199
+ Args:
200
+ xml_content: 语音消息的XML内容
201
+
202
+ Returns:
203
+ VoiceInfo 语音信息
204
+ """
205
+ try:
206
+ root = ET.fromstring(xml_content)
207
+ voicemsg = root.find('voicemsg')
208
+ if voicemsg is not None:
209
+ return VoiceInfo(
210
+ voicelength=int(voicemsg.get('voicelength', 0))
211
+ )
212
+ return None
213
+ except Exception as e:
214
+ logger.error(f"解析语音XML失败: {e}")
215
+ return None