Queer European MD passionate about IT

helper.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. package helper
  2. import (
  3. "fmt"
  4. "log"
  5. "math"
  6. "os"
  7. "os/signal"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/go-vgo/robotgo"
  12. "gopkg.in/yaml.v3"
  13. )
  14. type Point struct {
  15. X int
  16. Y int
  17. }
  18. func (p Point) String() string {
  19. return fmt.Sprintf("(%v, %v)", p.X, p.Y)
  20. }
  21. func (p Point) Click(button ...string) {
  22. buttonToClick := "left"
  23. if len(button) > 0 && (button[0] == "left" || button[0] == "right") {
  24. buttonToClick = button[0]
  25. }
  26. robotgo.Move(p.X, p.Y)
  27. robotgo.Click(buttonToClick, false)
  28. }
  29. func (p Point) RightClick() {
  30. p.Click("right")
  31. }
  32. func (p1 *Point) GetDistance(p2 *Point) float64 {
  33. return math.Sqrt(math.Pow(float64(p1.X-p2.X), float64(2)) + math.Pow(float64(p1.Y-p2.Y), float64(2)))
  34. }
  35. func (p0 *Point) GetDistanceFromClosest(pp ...*Point) float64 {
  36. var minDistance float64
  37. for index, p := range pp {
  38. if distance := p.GetDistance(p0); index == 0 || distance < minDistance {
  39. minDistance = distance
  40. }
  41. }
  42. return minDistance
  43. }
  44. type PointGroup struct {
  45. Name string
  46. Points []*Point
  47. }
  48. type SavedGroups struct {
  49. Groups []PointGroup
  50. }
  51. func getSavedGroups(yamlFile string) *SavedGroups {
  52. s := SavedGroups{}
  53. inputFile, err := os.ReadFile(yamlFile)
  54. if err == nil { // No yaml file found
  55. err = yaml.Unmarshal(inputFile, &s)
  56. if err != nil {
  57. log.Fatalf("%v - Exiting", err)
  58. }
  59. }
  60. for index, g := range s.Groups {
  61. if index == 0 {
  62. fmt.Println("Available groups:")
  63. }
  64. fmt.Printf("%v. %v\n", index+1, g.Name)
  65. }
  66. return &s
  67. }
  68. func addSavedGroup(s *SavedGroups) {
  69. g := PointGroup{}
  70. var name string
  71. var points int
  72. for {
  73. name = ""
  74. fmt.Print("If you want to add a new group of locations, enter a name. Press enter to skip. ")
  75. fmt.Scanln(&name)
  76. if name == "" {
  77. break
  78. }
  79. fmt.Printf("How many positions are there in group %v? ", name)
  80. _, err := fmt.Scanln(&points)
  81. if err != nil {
  82. fmt.Println("I haven't understood, try again...")
  83. var discard string
  84. fmt.Scanln(&discard)
  85. continue
  86. }
  87. for i := 0; i < points; i++ {
  88. fmt.Printf("Please move your mouse into position %v of %v (group %v)\n", i+1, points, name)
  89. time.Sleep(2 * time.Second)
  90. x, y := robotgo.GetMousePos()
  91. fmt.Printf("Added point (x=%v, y=%v) to %v\n", x, y, name)
  92. p := Point{X: x, Y: y}
  93. g.Points = append(g.Points, &p)
  94. }
  95. g.Name = name
  96. s.Groups = append(s.Groups, g)
  97. }
  98. }
  99. func storeSavedGroups(s *SavedGroups, yamlFile string) {
  100. d, err := yaml.Marshal(s)
  101. if err != nil {
  102. log.Fatalf("error: %v", err)
  103. }
  104. err = os.WriteFile(yamlFile, d, 0644)
  105. if err != nil {
  106. log.Fatalf("error: %v", err)
  107. }
  108. }
  109. func AbsInt(i int) int {
  110. if i > 0 {
  111. return i
  112. }
  113. return -i
  114. }
  115. func Oscillator(amplitude int) chan int {
  116. amplitude--
  117. c := make(chan int, 1)
  118. i := amplitude
  119. go func() {
  120. for {
  121. c <- AbsInt((i%(amplitude*2))-amplitude) + 1
  122. i++
  123. if i == amplitude*2 {
  124. i = 0
  125. }
  126. }
  127. }()
  128. return c
  129. }
  130. func StringOscillator(s string, amplitude int) chan string {
  131. c := make(chan string)
  132. o := Oscillator(amplitude)
  133. go func() {
  134. for {
  135. c <- strings.Repeat(s, <-o)
  136. }
  137. }()
  138. return c
  139. }
  140. func press(key string) {
  141. robotgo.KeyTap(key)
  142. }
  143. func keyboardWrite(s string) {
  144. robotgo.TypeStr(s)
  145. robotgo.MilliSleep(100)
  146. }
  147. func ClickRepeatedly(s *SavedGroups, c chan os.Signal) {
  148. for index, g := range s.Groups {
  149. fmt.Printf("- Press %v to select %v\n", index+1, g.Name)
  150. }
  151. var choice int
  152. for choice < 1 {
  153. fmt.Print("Your selection: ")
  154. _, err := fmt.Scanln(&choice)
  155. if err != nil {
  156. fmt.Println("I haven't understood, try again...")
  157. var discard string
  158. fmt.Scanln(&discard)
  159. continue
  160. }
  161. if choice > len(s.Groups) {
  162. choice = 0
  163. }
  164. }
  165. selection := s.Groups[choice-1]
  166. fmt.Printf("You selected %v. I'll be clicking in the following positions:\n", selection.Name)
  167. for index, p := range selection.Points {
  168. fmt.Printf("%v - %v\n", index+1, p)
  169. }
  170. o := StringOscillator(".", 15)
  171. var t int
  172. n := 1
  173. var minDistance float64
  174. for {
  175. for i, p := range selection.Points {
  176. if selection.Name == "akoya" { // hardcoded for Akoya presentation. #TODO: better handling (selection action list)
  177. if i == 0 {
  178. p.RightClick()
  179. time.Sleep(100 * time.Millisecond)
  180. press("v")
  181. time.Sleep(500 * time.Millisecond)
  182. } else if i == 1 {
  183. time.Sleep(200 * time.Millisecond)
  184. p.Click()
  185. time.Sleep(200 * time.Millisecond)
  186. keyboardWrite(strconv.Itoa(n))
  187. n++
  188. time.Sleep(100 * time.Millisecond)
  189. press("enter")
  190. time.Sleep(500 * time.Millisecond)
  191. } else {
  192. p.Click()
  193. time.Sleep(100 * time.Millisecond)
  194. }
  195. } else {
  196. p.Click()
  197. }
  198. time.Sleep(100 * time.Millisecond)
  199. }
  200. fmt.Println(<-o)
  201. time.Sleep(500 * time.Millisecond)
  202. t = 0
  203. for t = 0; t < 10; t++ {
  204. x, y := robotgo.GetMousePos()
  205. currentPosition := Point{X: x, Y: y}
  206. minDistance = currentPosition.GetDistanceFromClosest(selection.Points...)
  207. if minDistance <= 50 {
  208. break
  209. }
  210. fmt.Printf("%v You moved the mouse (distance %.2f), waiting 2 seconds (%v of 10)\n", strings.Repeat("_", 10-t), minDistance, t+1)
  211. time.Sleep(2 * time.Second)
  212. }
  213. if t >= 10 {
  214. fmt.Println("You stayed away too long, exiting...")
  215. c <- os.Interrupt
  216. return
  217. }
  218. }
  219. }
  220. func cleanUp(c chan os.Signal) chan int {
  221. r := make(chan int)
  222. go func() {
  223. signal := <-c
  224. fmt.Printf("Got signal %v, cleaning up...\n", signal)
  225. r <- 1
  226. }()
  227. return r
  228. }
  229. func interactWithUser(s *SavedGroups, yamlFile string, c chan os.Signal) {
  230. addSavedGroup(s)
  231. storeSavedGroups(s, yamlFile)
  232. ClickRepeatedly(s, c)
  233. }
  234. func RunAutoClicker() {
  235. c := make(chan os.Signal, 1)
  236. quit := cleanUp(c)
  237. signal.Notify(c, os.Interrupt)
  238. yamlFile := "SavedGroups.yaml"
  239. s := getSavedGroups(yamlFile)
  240. go interactWithUser(s, yamlFile, c)
  241. if <-quit == 1 {
  242. fmt.Println("Exiting...")
  243. } else {
  244. fmt.Println("Unknown error...")
  245. }
  246. }