11using  System . IO ; 
22using  System . Net . Http ; 
3+ using  System . Xml . Linq ; 
34
45namespace  BotSharp . Plugin . WebDriver . Drivers . PlaywrightDriver ; 
56
@@ -8,33 +9,66 @@ public partial class PlaywrightWebDriver
89    public  async  Task  DoAction ( MessageInfo  message ,  ElementActionArgs  action ,  BrowserActionResult  result ) 
910    { 
1011        var  page  =  _instance . GetPage ( message . ContextId ) ; 
11-         if  ( string . IsNullOrEmpty ( result . Selector ) ) 
12+         if  ( string . IsNullOrEmpty ( result . Selector )   &&   action . Position   ==   null ) 
1213        { 
1314            Serilog . Log . Error ( $ "Selector is not set.") ; 
1415            return ; 
1516        } 
1617
17-         ILocator  locator  =  page . Locator ( result . Selector ) ; 
18-         var  count  =  await  locator . CountAsync ( ) ; 
19- 
20-         if  ( count  ==  0 ) 
21-         { 
22-             Serilog . Log . Error ( $ "Element not found: { result . Selector } ") ; 
23-             return ; 
24-         } 
25-         else  if  ( count  >  1 ) 
18+         ILocator ?  locator ; 
19+         
20+         if  ( result . Selector  !=  null ) 
2621        { 
27-             if  ( ! action . FirstIfMultipleFound ) 
22+             locator  =  page . Locator ( result . Selector ) ; 
23+ 
24+             var  count  =  await  locator . CountAsync ( ) ; 
25+ 
26+             if  ( count  ==  0 ) 
2827            { 
29-                 Serilog . Log . Error ( $ "Multiple eElements were  found: { result . Selector } ") ; 
28+                 Serilog . Log . Error ( $ "Element not  found: { result . Selector } ") ; 
3029                return ; 
3130            } 
32-             else 
31+             else   if   ( count   >   1 ) 
3332            { 
34-                 locator  =  page . Locator ( result . Selector ) . First ; // 匹配到多个时取第一个,否则当await locator.ClickAsync();匹配到多个就会抛异常。 
33+                 if  ( ! action . FirstIfMultipleFound ) 
34+                 { 
35+                     Serilog . Log . Error ( $ "Multiple eElements were found: { result . Selector } ") ; 
36+                     return ; 
37+                 } 
38+                 else 
39+                 { 
40+                     locator  =  page . Locator ( result . Selector ) . First ; // 匹配到多个时取第一个,否则当await locator.ClickAsync();匹配到多个就会抛异常。 
41+                 } 
3542            } 
43+ 
44+             await  ExecuteAction ( message ,  page ,  locator ,  action ) ; 
3645        } 
46+         else  if  ( action . Position  !=  null  &&  action . Position . X  !=  0  &&  action . Position . Y  !=  0 ) 
47+         { 
48+             if  ( action . Position  !=  null  &&  action . Position . X  !=  0  &&  action . Position . Y  !=  0 ) 
49+             { 
50+                 var  elementHandle  =  await  page . EvaluateHandleAsync ( 
51+                     @"(coords) => document.elementFromPoint(coords.x, coords.y)" , 
52+                     new  {  x  =  ( int ) action . Position . X ,  y  =  ( int ) action . Position . Y  } 
53+                 ) ; 
3754
55+                 await  ExecuteAction ( message ,  page ,  elementHandle . AsElement ( ) ,  action ) ; 
56+             } 
57+         } 
58+         else 
59+         { 
60+             Serilog . Log . Error ( $ "Selector or position is not set.") ; 
61+             return ; 
62+         } 
63+ 
64+         if  ( action . WaitTime  >  0 ) 
65+         { 
66+             await  Task . Delay ( 1000  *  action . WaitTime ) ; 
67+         } 
68+     } 
69+ 
70+     private  async  Task  ExecuteAction ( MessageInfo  message ,  IPage  page ,  ILocator  locator ,  ElementActionArgs  action ) 
71+     { 
3872        if  ( action . Action  ==  BroswerActionEnum . Click ) 
3973        { 
4074            if  ( action . Position  ==  null ) 
@@ -201,12 +235,174 @@ await locator.ClickAsync(new LocatorClickOptions
201235                } 
202236            } 
203237        } 
238+     } 
204239
205-         if  ( action . WaitTime  >  0 ) 
240+     private  async  Task  ExecuteAction ( MessageInfo  message ,  IPage  page ,  IElementHandle  elementHandle ,  ElementActionArgs  action ) 
241+     { 
242+         var  body  =  page . Locator ( "body" ) ; 
243+ 
244+         if  ( action . Action  ==  BroswerActionEnum . Click ) 
206245        { 
246+             await  body . ClickAsync ( new  LocatorClickOptions 
247+             { 
248+                 Position  =  new  Position 
249+                 { 
250+                     X  =  action . Position . X , 
251+                     Y  =  action . Position . Y 
252+                 } 
253+             } ) ; 
254+         } 
255+         else  if  ( action . Action  ==  BroswerActionEnum . DropDown ) 
256+         { 
257+             var  tagName  =  await  body . EvaluateAsync < string > ( "el => el.tagName.toLowerCase()" ) ; 
258+             if  ( tagName  ==  "select" ) 
259+             { 
260+                 await  HandleSelectDropDownAsync ( page ,  body ,  action ) ; 
261+             } 
262+             else 
263+             { 
264+                 await  body . ClickAsync ( ) ; 
265+                 if  ( ! string . IsNullOrWhiteSpace ( action . PressKey ) ) 
266+                 { 
267+                     await  page . Keyboard . PressAsync ( action . PressKey ) ; 
268+                     await  page . Keyboard . PressAsync ( "Enter" ) ; 
269+                 } 
270+                 else 
271+                 { 
272+                     var  optionLocator  =  page . Locator ( $ "//div[text()='{ action . Content } ']") ; 
273+                     var  optionCount  =  await  optionLocator . CountAsync ( ) ; 
274+                     if  ( optionCount  ==  0 ) 
275+                     { 
276+                         Serilog . Log . Error ( $ "Dropdown option not found: { action . Content } ") ; 
277+                         return ; 
278+                     } 
279+                     await  optionLocator . First . ClickAsync ( ) ; 
280+                 } 
281+             } 
282+         } 
283+         else  if  ( action . Action  ==  BroswerActionEnum . InputText ) 
284+         { 
285+             await  elementHandle . FillAsync ( action . Content ) ; 
286+ 
287+             if  ( action . PressKey  !=  null ) 
288+             { 
289+                 if  ( action . DelayBeforePressingKey  >  0 ) 
290+                 { 
291+                     await  Task . Delay ( action . DelayBeforePressingKey ) ; 
292+                 } 
293+                 await  body . PressAsync ( action . PressKey ) ; 
294+             } 
295+         } 
296+         else  if  ( action . Action  ==  BroswerActionEnum . FileUpload ) 
297+         { 
298+             var  _states  =  _services . GetRequiredService < IConversationStateService > ( ) ; 
299+             var  files  =  new  List < string > ( ) ; 
300+             if  ( action . FileUrl  !=  null  &&  action . FileUrl . Length  >  0 ) 
301+             { 
302+                 files . AddRange ( action . FileUrl ) ; 
303+             } 
304+             var  hooks  =  _services . GetServices < IWebDriverHook > ( ) ; 
305+             foreach  ( var  hook  in  hooks ) 
306+             { 
307+                 files . AddRange ( await  hook . GetUploadFiles ( message ) ) ; 
308+             } 
309+             if  ( files . Count  ==  0 ) 
310+             { 
311+                 Serilog . Log . Warning ( $ "No files found to upload: { action . Content } ") ; 
312+                 return ; 
313+             } 
314+             var  fileChooser  =  await  page . RunAndWaitForFileChooserAsync ( async  ( )  => 
315+             { 
316+                 await  body . ClickAsync ( ) ; 
317+             } ) ; 
318+             var  guid  =  Guid . NewGuid ( ) . ToString ( ) ; 
319+             var  directory  =  Path . Combine ( Path . GetTempPath ( ) ,  guid ) ; 
320+             DeleteDirectory ( directory ) ; 
321+             Directory . CreateDirectory ( directory ) ; 
322+             var  localPaths  =  new  List < string > ( ) ; 
323+             var  http  =  _services . GetRequiredService < IHttpClientFactory > ( ) ; 
324+             using  var  httpClient  =  http . CreateClient ( ) ; 
325+             foreach  ( var  fileUrl  in  files ) 
326+             { 
327+                 try 
328+                 { 
329+                     using  var  fileData  =  await  httpClient . GetAsync ( fileUrl ) ; 
330+                     var  fileName  =  new  Uri ( fileUrl ) . AbsolutePath ; 
331+                     var  localPath  =  Path . Combine ( directory ,  Path . GetFileName ( fileName ) ) ; 
332+                     await  using  var  fs  =  new  FileStream ( localPath ,  FileMode . Create ,  FileAccess . Write ,  FileShare . None ) ; 
333+                     await  fileData . Content . CopyToAsync ( fs ) ; 
334+                     localPaths . Add ( localPath ) ; 
335+                 } 
336+                 catch  ( Exception  ex ) 
337+                 { 
338+                     Serilog . Log . Error ( $ "FileUpload failed for { fileUrl } . Message: { ex . Message } ") ; 
339+                 } 
340+             } 
341+             await  fileChooser . SetFilesAsync ( localPaths ) ; 
207342            await  Task . Delay ( 1000  *  action . WaitTime ) ; 
208343        } 
344+         else  if  ( action . Action  ==  BroswerActionEnum . Typing ) 
345+         { 
346+             await  body . PressSequentiallyAsync ( action . Content ) ; 
347+             if  ( action . PressKey  !=  null ) 
348+             { 
349+                 if  ( action . DelayBeforePressingKey  >  0 ) 
350+                 { 
351+                     await  Task . Delay ( action . DelayBeforePressingKey ) ; 
352+                 } 
353+                 await  body . PressAsync ( action . PressKey ) ; 
354+             } 
355+         } 
356+         else  if  ( action . Action  ==  BroswerActionEnum . Hover ) 
357+         { 
358+             await  body . HoverAsync ( ) ; 
359+         } 
360+         else  if  ( action . Action  ==  BroswerActionEnum . DragAndDrop ) 
361+         { 
362+             // Locate the element to drag 
363+             var  box  =  await  body . BoundingBoxAsync ( ) ; 
364+ 
365+             if  ( box  !=  null ) 
366+             { 
367+                 // Calculate start position 
368+                 float  startX  =  box . X  +  box . Width  /  2 ;  // Start at the center of the element 
369+                 float  startY  =  box . Y  +  box . Height  /  2 ; 
370+ 
371+                 // Drag offsets 
372+                 float  offsetX  =  action . Position . X ; 
373+                 // Move horizontally 
374+                 if  ( action . Position . Y  ==  0 ) 
375+                 { 
376+                     // Perform drag-and-move 
377+                     // Move mouse to the start position 
378+                     var  mouse  =  page . Mouse ; 
379+                     await  mouse . MoveAsync ( startX ,  startY ) ; 
380+                     await  mouse . DownAsync ( ) ; 
381+ 
382+                     // Move mouse smoothly in increments 
383+                     var  tracks  =  GetVelocityTrack ( offsetX ) ; 
384+                     foreach  ( var  track  in  tracks ) 
385+                     { 
386+                         startX  +=  track ; 
387+                         await  page . Mouse . MoveAsync ( startX ,  0 ,  new  MouseMoveOptions 
388+                         { 
389+                             Steps  =  3 
390+                         } ) ; 
391+                     } 
392+ 
393+                     // Release mouse button 
394+                     await  Task . Delay ( 1000 ) ; 
395+                     await  mouse . UpAsync ( ) ; 
396+                 } 
397+                 else 
398+                 { 
399+                     throw  new  NotImplementedException ( ) ; 
400+                 } 
401+             } 
402+         } 
209403    } 
404+ 
405+ 
210406    private  void  DeleteDirectory ( string  directory ) 
211407    { 
212408        if  ( Directory . Exists ( directory ) ) 
0 commit comments