{"id":13586050,"url":"https://github.com/hofstadter-io/self-driving-desktop","last_synced_at":"2025-08-23T13:25:56.324Z","repository":{"id":57465447,"uuid":"185474091","full_name":"hofstadter-io/self-driving-desktop","owner":"hofstadter-io","description":"Desktop Automation Framework. Drive your mouse and keyboard with an easy to use language.","archived":false,"fork":false,"pushed_at":"2019-11-07T09:05:45.000Z","size":46,"stargazers_count":583,"open_issues_count":6,"forks_count":35,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-07-10T02:52:06.826Z","etag":null,"topics":["desktop-automation","domain-specific-language","keyboard-automation","lark-parser","mouse-movement","pyautogui","python","recording"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hofstadter-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-07T20:34:29.000Z","updated_at":"2025-04-23T04:18:34.000Z","dependencies_parsed_at":"2022-09-13T13:40:48.523Z","dependency_job_id":null,"html_url":"https://github.com/hofstadter-io/self-driving-desktop","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/hofstadter-io/self-driving-desktop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hofstadter-io%2Fself-driving-desktop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hofstadter-io%2Fself-driving-desktop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hofstadter-io%2Fself-driving-desktop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hofstadter-io%2Fself-driving-desktop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hofstadter-io","download_url":"https://codeload.github.com/hofstadter-io/self-driving-desktop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hofstadter-io%2Fself-driving-desktop/sbom","scorecard":{"id":467425,"data":{"date":"2025-08-11","repo":{"name":"github.com/hofstadter-io/self-driving-desktop","commit":"309b9b6614f8d3f2b85ed40c8e3cd9d72cd069a6"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 1/24 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T12:53:36.563Z","repository_id":57465447,"created_at":"2025-08-19T12:53:36.563Z","updated_at":"2025-08-19T12:53:36.563Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271749048,"owners_count":24814115,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["desktop-automation","domain-specific-language","keyboard-automation","lark-parser","mouse-movement","pyautogui","python","recording"],"created_at":"2024-08-01T15:05:17.870Z","updated_at":"2025-08-23T13:25:56.272Z","avatar_url":"https://github.com/hofstadter-io.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# self-driving-desktop\n\nDesktop Automation Framework.\nDrive your keyboard and mouse with text files.\n\n```\npip3 install self-driving-desktop\n```\n\n### Running\n\nPlaying a playlist:\n\n```\nsdd playlist.txt\n```\n\nRecording a playlist:\n\n```\nsdd --record recording.txt\n```\n\n### Playlists\n\n```\n# Import other playlist files\nimport \"test/main.txt\";\n\n# Create Coordinates\ncoords {\n  \"center\": {\n    \"1080p\": [960, 540],\n    \"720p\":  [640, 360]\n  }\n};\n\n# Create a playlist\nplaylist \"openChrome\" {\n  # Run programs in the shell\n  shell \"google-chrome\";\n  sleep 2.0;\n\n  # Name the new window\n  active \"hofChrome\";\n  sleep 0.5;\n\n  # Use hotkeys to arrange\n  hotkeys \"winleft\" \"right\";\n  sleep 1;\n};\n\nplaylist \"closeChrome\" {\n  # Focus a named window\n  focus \"hofChrome\";\n  hotkeys \"alt\" \"f4\";\n  sleep 1;\n};\n\nplaylist \"readTheDocs\" {\n  # Go to a webpage\n  focus \"hofChrome\";\n  sleep 0.2;\n\n  # Type the URL\n  write \"https://docs.hofstadter.io\\n\" 0.05;\n\n  # Goto an imported coordinate\n  coord \"getting-started\" 0.5;\n};\n\n# Move the mouse in a square\nplaylist \"repeatTest\" {\n  mm 100 100 1;\n  mm 1000 100 1;\n  mm 1000 500 1;\n  mm 100 500 1;\n};\n\n# Our main playlist\nplaylist \"main\" {\n  # Goto a named coordinate, also with offset\n  coord \"center\" 1;\n  coord \"center\" 250 -250 1;\n\n  # Operate the browser\n  play \"openChrome\";\n  play \"readTheDocs\";\n  play \"closeChrome\";\n\n  # Play a playlist multiple times\n  play \"repeatTest\" 4;\n\n};\n\n# Set screen size\nscreen \"1080p\";\n\n# Set the global delay between steps\ndelay 0.025;\n\n# Finally, play our main playlist\nplay \"main\";\n\n```\n\n### Grammar\n\n#### Top-level:\n\n- file has steps and playlists\n- steps are the only thing run\n- play runs a playlist\n\n```\n# relative imports from file\nimport \"relative/path.txt\";\n\n# named coordinates\ncoords {\n  # coord name\n  \"center\": {\n    # screen identifier\n    \"1080p\": [960, 540],\n    \"720p\":  [640, 360]\n  }\n};\n\n# define playlists\nplaylist \"my-playlist\" {\n  steps...;\n};\n\nplaylist \"main\" {\n  steps...;\n  # run playlists from playlists\n  play \"my-playlist\"\n}\n\n# set the screen identifier\nscreen \"1080p\";\n\n# run a playlist\nplay \"my-playlist\" \"main\";\n```\n\n#### Steps:\n\n- `play \"name\" \"nameB\" ... [N];`: run one or more playlists, optionally repeat N times.\n- `delay x.y;`: set delay between steps to x.y seconds\n- `sleep x.y;`: sleep for x.y seconds\n- `screen \"screen\";`: set the screen resolution identifier\n- `shell \"quoted strings\"+;`: exec a command from the program\n\nwindows:\n\n- `active \"someName\";`: name the active window\n- `focus \"someName\";`: focus a named window\n\nmouse:\n\n- `mouse x y s;`: move the mouse to x,y in s seconds\n- `coord \"name\" s;`: move the mouse to a named coordinate in s seconds\n- `coord \"name\" x y s;`: move to a named coordinate with offset in s seconds\n- `click;`: click the left mouse button\n- `btnclick [left,middle,right];`\n- `btndown [left,middle,right];`\n- `btnup [left,middle,right];`\n- `drag [left,middle,right] x y s;`: drag the mouse to x,y in s seconds\n- `scroll n;`: scroll n lines, negative is up\n- `hscroll n;`: horizontal scroll n \"clicks\", negative is left\n\nkeyboard:\n\n- `keypress \"key\";`\n- `keydown \"key\";`\n- `keyup \"key\";`\n- `hotkeys \"quoted\" \"keys\" ...;`: press some keys together\n- `write \"quoted string\\n\";`: type a string, \"\\n\" is enter\n\nclipboard:\n\n- `copy;`, just `ctrl-c`\n- `paste;`, just `ctrl-v`\n- `save_clipboard \"name\";` save the clipboard contents to \"name\"\n- `load_clipboard \"name\";` load the clipboard contents from \"name\"\n- `copy_clipboard \"name\";` copy \u0026\u0026 save the clipboard contents to \"name\"\n- `paste_clipboard \"name\";` load the clipboard contents from \"name\" \u0026\u0026 paste\n\nall keys are from [pyautogui](https://pyautogui.readthedocs.io/en/latest/keyboard.html#keyboard-keys)\n\n[Grammer Definition](./self_driving_desktop/grammar.py)\n\n### Recording\n\nYou can record your mouse and keyboard\nto a playlist file by:\n\n```\nsdd record.txt --record\n```\n\n_Note, not all keys are working yet._\n\nA keymap to fix some is [here](./self_driving_desktop/keymap.py).\n\n### Development Setup\n\n```\nvirtualenv --python python3 penv\nsource penv/bin/activate\npip install -r requirements.txt\nexport PYTHONPATH=.\npython self_driving_desktop/__main__.py ...\n```\n\nInstall from local repository:\n\n```\ngit clone https://github.com/hofstadter-io/self-driving-desktop\npip3 install ./self-driving-desktop/\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhofstadter-io%2Fself-driving-desktop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhofstadter-io%2Fself-driving-desktop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhofstadter-io%2Fself-driving-desktop/lists"}