{"id":18631751,"url":"https://github.com/esotericpig/jeso","last_synced_at":"2026-05-20T14:09:35.732Z","repository":{"id":96103871,"uuid":"182838029","full_name":"esotericpig/jeso","owner":"esotericpig","description":"☕🐚🍯 Java utils to make Java less verbose and more fun.","archived":false,"fork":false,"pushed_at":"2024-09-06T04:20:42.000Z","size":450,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-27T07:34:25.220Z","etag":null,"topics":["gradle","java","java-awt-robot","java-library","java-utilities","java-utils","javautils","robot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/esotericpig.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-22T17:45:55.000Z","updated_at":"2024-09-06T04:20:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"2d71c4c4-0d69-4c37-887a-f1d2745cf3c1","html_url":"https://github.com/esotericpig/jeso","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esotericpig%2Fjeso","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esotericpig%2Fjeso/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esotericpig%2Fjeso/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/esotericpig%2Fjeso/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/esotericpig","download_url":"https://codeload.github.com/esotericpig/jeso/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239425427,"owners_count":19636346,"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","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":["gradle","java","java-awt-robot","java-library","java-utilities","java-utils","javautils","robot"],"created_at":"2024-11-07T05:08:30.711Z","updated_at":"2026-05-20T14:09:30.697Z","avatar_url":"https://github.com/esotericpig.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jeso\n\n[![JitPack](https://img.shields.io/jitpack/version/com.github.esotericpig/jeso?color=%23334A5E)](https://jitpack.io/#esotericpig/jeso)\n[![Javadoc](https://img.shields.io/badge/doc-javadoc-%23A0522D.svg)](https://esotericpig.github.io/docs/jeso/javadoc/index.html)\n[![Source Code](https://img.shields.io/badge/source-github-%23211F1F.svg)](https://github.com/esotericpig/jeso)\n[![License](https://img.shields.io/github/license/esotericpig/jeso.svg)](LICENSE)\n\nJava utils to make Java less verbose and more fun.\n\nInspired largely by Ruby.\n\nName = Java + gesso + esoteric.\n\n## Contents\n\n- [Requirements](#requirements)\n- [Setup](#setup)\n    - [JitPack](#jitpack)\n    - [GitHub Packages](#github-packages)\n    - [Manually](#manually)\n    - [Pre-Release](#pre-release)\n- [Using](#using)\n    - [Top Package](#top-package)\n    - [BotBuddy Package](#botbuddy-package)\n    - [Code Package](#code-package)\n    - [IO Package](#io-package)\n- [Hacking](#hacking)\n- [License](#license)\n\n## [Requirements](#contents)\n\n- Java 8 or later\n\n## [Setup](#contents)\n\nPick your poison...\n\n### [JitPack](#contents)\n\nThis is the recommended and easiest way.\n\nSee [jitpack.io/#esotericpig/jeso](https://jitpack.io/#esotericpig/jeso).\n\nHere's an example using Gradle:\n\n```Groovy\n// If in 'settings.gradle', use `dependencyResolutionManagement{}`.\n// If in 'build.gradle', don't use `dependencyResolutionManagement{}`, but just `repositories{}`.\ndependencyResolutionManagement {\n  repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n\n  repositories {\n    //mavenCentral()\n    //google()\n    maven { url 'https://jitpack.io' }\n    mavenLocal()\n  }\n}\n\n// In 'build.gradle':\ndependencies {\n  implementation 'com.github.esotericpig:jeso:0.3.10'\n}\n```\n\nOptionally, for [security reasons](https://docs.jitpack.io/intro/#building-with-jitpack), you can also add excludes to the non-JitPack repos:\n\n```Groovy\ndependencyResolutionManagement {\n  repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n\n  repositories {\n    mavenCentral { content { excludeGroupByRegex \"com\\\\.github\\\\.esotericpig.*\" } }\n    google { content { excludeGroupByRegex \"com\\\\.github\\\\.esotericpig.*\" } }\n    maven { url 'https://jitpack.io' }\n    mavenLocal()\n  }\n}\n```\n\n### [GitHub Packages](#contents)\n\nYou can view the GitHub packages for this project [here](https://github.com/esotericpig/jeso/packages).\n\n#### [Gradle](#contents)\n\nSee [here](https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-gradle-for-use-with-github-packages) for more information.\n\nIf you don't want to use your GitHub password (recommended), first [create a token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line#creating-a-token) with at least the **read:packages** scope.\n\nIn **~/.gradle/gradle.properties**:\n```Properties\ngpr.user=username # Your GitHub username\ngpr.key=token     # Your GitHub token (or password)\n```\n\nIn your project's **build.gradle**:\n```Groovy\nrepositories {\n  maven {\n    name = 'Jeso GitHub Package'\n    url = uri('https://maven.pkg.github.com/esotericpig/jeso')\n\n    credentials {\n      username = project.findProperty('gpr.user') ?: System.getenv(\"USERNAME\")\n      password = project.findProperty('gpr.key') ?: System.getenv(\"PASSWORD\")\n    }\n  }\n}\n\ndependencies {\n  // TODO: Edit the version appropriately!\n  implementation 'com.github.esotericpig:jeso:X.X.X'\n}\n```\n\n#### [Maven](#contents)\n\nSee [here](https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-apache-maven-for-use-with-github-packages) for more information.\n\nIf you don't want to use your GitHub password (recommended), first [create a token](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line#creating-a-token) with at least the **read:packages** scope.\n\nIn **~/.m2/settings.xml**:\n```XML\n\u003csettings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n  xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0\n                      http://maven.apache.org/xsd/settings-1.0.0.xsd\"\u003e\n\n  \u003cactiveProfiles\u003e\n    \u003cactiveProfile\u003egithub\u003c/activeProfile\u003e\n  \u003c/activeProfiles\u003e\n\n  \u003cprofiles\u003e\n    \u003cprofile\u003e\n      \u003cid\u003egithub\u003c/id\u003e\n\n      \u003crepositories\u003e\n        \u003crepository\u003e\n          \u003cid\u003ecentral\u003c/id\u003e\n          \u003curl\u003ehttps://repo1.maven.org/maven2\u003c/url\u003e\n          \u003creleases\u003e\u003cenabled\u003etrue\u003c/enabled\u003e\u003c/releases\u003e\n          \u003csnapshots\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003c/snapshots\u003e\n        \u003c/repository\u003e\n\n        \u003crepository\u003e\n          \u003cid\u003egithub\u003c/id\u003e\n          \u003cname\u003eJeso GitHub Package\u003c/name\u003e\n          \u003curl\u003ehttps://maven.pkg.github.com/esotericpig/jeso\u003c/url\u003e\n        \u003c/repository\u003e\n      \u003c/repositories\u003e\n    \u003c/profile\u003e\n  \u003c/profiles\u003e\n\n  \u003cservers\u003e\n    \u003cserver\u003e\n      \u003cid\u003egithub\u003c/id\u003e\n      \u003c!-- TODO: Your GitHub username --\u003e\n      \u003cusername\u003eUSERNAME\u003c/username\u003e\n      \u003c!-- TODO: Your GitHub token (or password) --\u003e\n      \u003cpassword\u003eTOKEN\u003c/password\u003e\n    \u003c/server\u003e\n  \u003c/servers\u003e\n\u003c/settings\u003e\n```\n\nIn your project's **pom.xml**:\n```XML\n\u003cdependencies\u003e\n  \u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.esotericpig\u003c/groupId\u003e\n    \u003cartifactId\u003ejeso\u003c/artifactId\u003e\n    \u003c!-- TODO: Edit the version appropriately! --\u003e\n    \u003cversion\u003eX.X.X\u003c/version\u003e\n  \u003c/dependency\u003e\n\u003c/dependencies\u003e\n```\n\nInstall the package*:\n```\n$ mvn install\n```\n\n*If you have bad internet, you'll need to call this multiple times until it downloads.\n\n### [Manually](#contents)\n\nDownload the **Assets** from the latest [Release](https://github.com/esotericpig/jeso/releases/latest).\n\nThen import the following files into your project:\n\n| Asset Files |\n| --- |\n| jeso-x.x.x.jar |\n| jeso-x.x.x-sources.jar |\n| jeso-x.x.x-javadoc.zip |\n\nAlternatively, you can just import this one file, but it also includes dependent jars (if any):\n\n| Asset Files |\n| --- |\n| jeso-x.x.x-all.jar |\n\n### [Pre-Release](#contents)\n\nTo build a pre-release, please do the following:\n\n```\n$ git clone 'https://github.com/esotericpig/jeso.git'\n$ cd jeso\n$ ./gradlew(.bat) clean buildRelease -x check -x test\n```\n\nYou can probably safely exclude *check* and *test* (like in the above example) to build it faster (i.e., to not download \u0026 install development/test dependencies), as those checks should have already been run when committing the code.\n\nThen import the following files into your project:\n\n| Release Files |\n| --- |\n| build/libs/jeso-x.x.x.jar |\n| build/libs/jeso-x.x.x-sources.jar |\n| build/distributions/jeso-x.x.x-javadoc.zip |\n\nAlternatively, you can use `publishToMavenLocal` and `mavenLocal()`:\n\n```\n$ ./gradlew(.bat) publishToMavenLocal\n\n// In 'settings.gradle':\ndependencyResolutionManagement {\n  repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n\n  repositories {\n    // ...\n    mavenLocal()\n  }\n}\n\n// In 'build.gradle':\ndependencies {\n  implementation 'com.github.esotericpig:jeso:0.3.10'\n}\n```\n\nAlternatively, you can build everything into one \"fat\" jar, which includes dependent jars (if any):\n\n```\n$ ./gradlew(.bat) clean buildFatRelease -x check -x test\n```\n\n| Release Files |\n| --- |\n| build/libs/jeso-x.x.x-all.jar |\n\n## [Using](#contents)\n\n[Jeso Javadoc](https://esotericpig.github.io/docs/jeso/javadoc/index.html)\n\n[Top Package](#top-package) [[Javadoc](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/package-summary.html)]\n\n| Class | Summary | Javadoc | File |\n| ----- | ------- | ------- | ---- |\n| [Arys](#arys) | Utility class for Arrays | [Arys.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/Arys.html) | [Arys.java](src/main/java/com/esotericpig/jeso/Arys.java) |\n| [Bools](#bools) | Utility class for Booleans | [Bools.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/Bools.html) | [Bools.java](src/main/java/com/esotericpig/jeso/Bools.java) |\n| [Duplicable](#duplicable) | Generic replacement for Cloneable/clone() | [Duplicable.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/Duplicable.html) | [Duplicable.java](src/main/java/com/esotericpig/jeso/Duplicable.java) |\n| [OSFamily](#osfamily) | Enum for guessing the OS family from a String | [OSFamily.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/OSFamily.html) | [OSFamily.java](src/main/java/com/esotericpig/jeso/OSFamily.java) |\n| [Strs](#strs) | Utility class for Strings | [Strs.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/Strs.html) | [Strs.java](src/main/java/com/esotericpig/jeso/Strs.java) |\n| [Sys](#sys) | Utility class for System | [Sys.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/Sys.html) | [Sys.java](src/main/java/com/esotericpig/jeso/Sys.java) |\n\n[BotBuddy Package](#botbuddy-package) [[Javadoc](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/botbuddy/package-summary.html)]\n\n| Class | Summary | Javadoc | File |\n| ----- | ------- | ------- | ---- |\n| [BotBuddy](#botbuddy) | Wrapper around [java.awt.Robot](https://docs.oracle.com/javase/8/docs/api/java/awt/Robot.html) | [BotBuddy.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/botbuddy/BotBuddy.html) | [BotBuddy.java](src/main/java/com/esotericpig/jeso/botbuddy/BotBuddy.java) |\n| BotBuddy.Shortcut | Functional interface for automatic operations for [BotBuddy](#botbuddy) | [BotBuddy.Shortcut.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/botbuddy/BotBuddy.Shortcut.html) | [BotBuddy.java#Shortcut](src/main/java/com/esotericpig/jeso/botbuddy/BotBuddy.java) |\n| [BotBuddyCode](#botbuddycode) | Very simple scripting \"language\" interpreter for [BotBuddy](#botbuddy) | [BotBuddyCode.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/botbuddy/BotBuddyCode.html) | [BotBuddyCode.java](src/main/java/com/esotericpig/jeso/botbuddy/BotBuddyCode.java) |\n| [BotBuddyCodeApp](#botbuddycodeapp) | Simple CLI app for [BotBuddyCode](#botbuddycode) that can take in a file or read piped-in input (pipeline) | [BotBuddyCodeApp.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/botbuddy/BotBuddyCodeApp.html) | [BotBuddyCodeApp.java](src/main/java/com/esotericpig/jeso/botbuddy/BotBuddyCodeApp.java) |\n\n[Code Package](#code-package) [[Javadoc](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/code/package-summary.html)]\n\n| Class | Summary | Javadoc | File |\n| ----- | ------- | ------- | ---- |\n| LineOfCode | Immutable class that stores a Line Number and Line Column | [LineOfCode.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/code/LineOfCode.html) | [LineOfCode.java](src/main/java/com/esotericpig/jeso/code/LineOfCode.java) |\n| ParseCodeException | Runtime Exception that can store a [LineOfCode](#lineofcode) and build a detailed message with it | [ParseCodeException.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/code/ParseCodeException.html) | [ParseCodeException.java](src/main/java/com/esotericpig/jeso/code/ParseCodeException.java) |\n\n[IO Package](#io-package) [[Javadoc](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/io/package-summary.html)]\n\n| Class | Summary | Javadoc | File |\n| ----- | ------- | ------- | ---- |\n| [StringListReader](#stringlistreader) | Reader for a list of Strings | [StringListReader.html](https://esotericpig.github.io/docs/jeso/javadoc/com/esotericpig/jeso/io/StringListReader.html) | [StringListReader.java](src/main/java/com/esotericpig/jeso/io/StringListReader.java) |\n\n### [Top Package](#using)\n\n#### [Arys](#using)\n\nA utility class for Arrays.\n\n```Groovy\nimport com.esotericpig.jeso.Arys;\n\nString[] breakfast = {\"coffee\",\"coffee\",null,\"eggs\",\"eggs\",null,\"toast\",\"turkey sausage\"};\nString[] newArray = null;\nRandom rand = new Random();\n\n// Remove nulls; varargs\n// - [coffee, coffee, eggs, eggs, toast, turkey sausage]\nprintln( Arrays.toString(Arys.compact(breakfast)) );\n\n// Move nulls to end; mutable\n// - [coffee, coffee, eggs, eggs, toast, turkey sausage, null, null]\nprintln( Arrays.toString(Arys.compactMut(breakfast)) );\n\n// Join into a String; varargs\n// - 123\nprintln( Arys.join(1,2,3) );\n\n// Join into a String with a custom separator; varargs\n// - coffee,coffee,eggs,eggs,toast,turkey sausage,null,null\n// - coffee | coffee | eggs | eggs | toast | turkey sausage | null | null\nprintln( Arys.joins(',',breakfast) );\nprintln( Arys.joins(\" | \",breakfast) );\n\n// Create a new array using Reflection\n// - [Ljava.lang.String;@][3]\nnewArray = Arys.newArray(breakfast,3);\nprintln( newArray + \"][\" + newArray.length + \"]\" );\n\n// Get a random element; varargs\n// - eggs\n// - coffee\nprintln( Arys.sample(breakfast) );\nprintln( Arys.sample(rand,breakfast) );\n\n// Get multiple random elements using a shuffle strategy (don't repeat); varargs\n// - [eggs, null, coffee]\n// - [coffee, null, turkey sausage, coffee, null, eggs, eggs, toast]\n// - [coffee, turkey sausage, eggs]\nprintln( Arrays.toString(Arys.samples(3,breakfast)) );\nprintln( Arrays.toString(Arys.samples(100,breakfast)) );\nprintln( Arrays.toString(Arys.samples(3,rand,breakfast)) );\n\n// Remove duplicate elements; varargs\n// - [coffee, eggs, toast, turkey sausage, null]\nprintln( Arrays.toString(Arys.unique(breakfast)) );\n\n// Remove duplicate elements (pad with nulls); mutable\n// - [coffee, eggs, toast, turkey sausage, null, null, null, null]\nprintln( Arrays.toString(Arys.uniqueMut(breakfast)) );\n\n// Create a new array from a List using Reflection\n// - [Ljava.lang.String;@][8]\nnewArray = Arys.toArray(breakfast,Arrays.asList(breakfast));\nprintln( newArray + \"][\" + newArray.length + \"]\" );\n```\n\n#### [Bools](#using)\n\nA utility class for Booleans.\n\n```Java\nimport com.esotericpig.jeso.Bools;\n\n// [\"1\",\"on\",\"t\",\"true\",\"y\",\"yes\"] are all true and case-insensitive\nBools.parse(\"On\"); // true\n```\n\n#### [Duplicable](#using)\n\nA Generic replacement for Cloneable/clone().\n\nAlmost every Library has their own, so let's reinvent the wheel.\n\n[Java Cloning: Even Copy Constructors Are Not Enough](https://dzone.com/articles/java-cloning-even-copy-constructors-are-not-suffic) [DZone]  \n[CopyConstructorExample.java](https://github.com/njnareshjoshi/exercises/blob/master/src/org/programming/mitra/exercises/CopyConstructorExample.java) [GitHub]\n\n```Java\nimport com.esotericpig.jeso.Duplicable;\n\npublic class Testbed\n  public static void main(String[] args) {\n    Alumnus alum1 = new Alumnus(\"Bob\",\"MySchool\",\"MyJob\");\n    Alumnus alum2 = alum1.dup();\n\n    // Same school\n    alum2.name = \"Fred\";\n    alum2.job = \"CoolJob\";\n\n    System.out.println(alum1);\n    System.out.println(alum2);\n  }\n}\n\nclass User implements Duplicable\u003cUser\u003e {\n  public String name;\n\n  public User(String name) { this.name = name; }\n  protected User(User user) { this.name = user.name; }\n\n  public User dup() { return new User(this); }\n}\n\nclass Student extends User {\n  public String school;\n\n  public Student(String name,String school) { super(name); this.school = school; }\n  protected Student(Student student) { super(student); this.school = student.school; }\n\n  @Override\n  public Student dup() { return new Student(this); }\n}\n\nclass Alumnus extends Student {\n  public String job;\n\n  public Alumnus(String name,String school,String job) { super(name,school); this.job = job; }\n  protected Alumnus(Alumnus alumnus) { super(alumnus); this.job = alumnus.job; }\n\n  @Override\n  public Alumnus dup() { return new Alumnus(this); }\n\n  @Override\n  public String toString() {\n    StringBuilder sb = new StringBuilder();\n\n    sb.append(name).append(\":\\t\").append('[');\n    sb.append(school).append(',');\n    sb.append(job).append(']');\n\n    return sb.toString();\n  }\n}\n```\n\n#### [OSFamily](#using)\n\nAn enum for guessing the [OS family](https://en.wikipedia.org/wiki/Category:Operating_system_families) from a String.\n\nMost users will only need [Sys.OS_FAMILY](#sys).\n\n```Groovy\nimport com.esotericpig.jeso.OSFamily;\n\nRandom rand = new Random();\n\n// Used for testing\nprintln( OSFamily.getRandValue(rand) );\n\n// - OSFamily.LINUX\n// - OSFamily.MACOS\n// - OSFamily.WINDOWS\n// - OSFamily.UNKNOWN\nprintln( OSFamily.guessFromName(\"GNU/Linux Fedora\") );\nprintln( OSFamily.guessFromName(\"Mac OS X\") );\nprintln( OSFamily.guessFromName(\"Microsoft Windows XP\") );\nprintln( OSFamily.guessFromName(\"TempleOS\") );\n```\n\n#### [Strs](#using)\n\nA utility class for Strings.\n\n```Groovy\nimport com.esotericpig.jeso.Strs;\n\n// Remove (left) leading whitespace; mutable\n// - 'Hello World   '\nprintln( \"'\" + Strs.ltrim(new StringBuilder(\"   Hello World   \")) + \"'\" );\n\n// Remove (right) trailing whitespace; mutable\n// - '   Hello World'\nprintln( \"'\" + Strs.rtrim(new StringBuilder(\"   Hello World   \")) + \"'\" );\n\n// Remove leading \u0026 trailing whitespace; mutable\n// - 'Hello World'\nprintln( \"'\" + Strs.trim(new StringBuilder(\"   Hello World   \")) + \"'\" );\n```\n\n#### [Sys](#using)\n\nA utility class for System.\n\n```Groovy\nimport com.esotericpig.jeso.Sys;\n\n// \"Unknown\" if not set\nprintln( Sys.OS_NAME );\n\n// Uses the OSFamily enum\nprintln( Sys.OS_FAMILY );\n\n// Gets a System property and ignores SecurityException if thrown\n// (will return the specified default value or null)\n// - If you use System.getProperty(...), then it can potentially throw a SecurityException, but\n//   you might want your app to continue to work even if \"os.name\" is blocked from being read\nprintln( Sys.getSafeProp(\"os.name\") ); // If not set, null is the default value\nprintln( Sys.getSafeProp(\"os.name\",\"Unknown\") );\n```\n\n### [BotBuddy Package](#using)\n\n#### [BotBuddy](#using)\n\nA wrapper around [java.awt.Robot](https://docs.oracle.com/javase/8/docs/api/java/awt/Robot.html).\n\n**Warning** for **Linux** users:  \n\n- On Wayland, Java's Robot will not work. You will need to either use X11 or XWayland, until either OpenJDK or Wayland is fixed.\n\nSee the [samples](samples/botbuddy) for a quick peek.\n\nIt can be used for...\n\n- Moving the mouse and pasting in text.\n- Automating tedious tasks that a website/program has not implemented yet, such as inputting a CSV file into a website, row by row.\n- Automating tedious tasks in games, such as moving your character to a place to fish and then moving your character home to dump the fish.\n- Testing/QAing your desktop applications.\n\nExample usage:\n\n```Java\nimport com.esotericpig.jeso.botbuddy.BotBuddy;\n\n// ...class...main...\n\nBotBuddy buddy = BotBuddy.builder().build();\n\nbuddy.paste(999,493,\"Fish\")\n     .enter(1427,500,\"Sakana\");\nbuddy.click(1853,1015)\n     .delayLong();\n```\n\nTo construct a class, the [Builder Design Pattern](https://en.wikipedia.org/wiki/Builder_pattern) is used:\n\n```Java\nBotBuddy buddy = BotBuddy.builder()\n                         .autoDelay(false)\n                         .fastDelay(33)\n                         .build();\n```\n\nMost methods can also be chained together:\n\n```Java\nbuddy.beep()\n     .beginFastMode()\n     .beginSafeMode()\n     .clearPressed()\n     .clearPressedButtons()\n     .clearPressedKeys()\n     .click([int button])\n     .click([int x,int y,int button])\n     .clicks(int... buttons)\n     .copy(String text,[ClipboardOwner owner])\n     .delay(int delay)\n     .delayAuto()\n     .delayFast()\n     .delayLong()\n     .delayShort()\n     .doubleClick([int button])\n     .doubleClick([int x,int y,int button])\n     .drag(int fromX,int fromY,int toX,int toY,[int button])\n     .endFastMode()\n     .endSafeMode()\n     .enter([String text])\n     .enter([int x,int y,String text])\n     .leftClick([int x,int y])\n     .middleClick([int x,int y])\n     .move(int x,int y)\n     .paste([String text])\n     .paste([int x,int y,String text])\n     .pressButton([int x,int y],int button)\n     .pressButtons(int... buttons)\n     .pressKey([int x,int y],int keyCode)\n     .pressKeys(int... keyCodes)\n     .releaseButton([int x,int y],int button)\n     .releaseButtons([int... buttons])\n     .releaseKey([int x,int y],int keyCode)\n     .releaseKeys([int... keyCodes])\n     .releasePressed()\n     .rightClick([int x,int y])\n     .rollButtons(int... buttons)\n     .rollKeys(int... keyCodes)\n     .shortcut(BotBuddy.Shortcut shortcut)\n     .stash()\n     .type([int x,int y],int keyCode)\n     .type([int x,int y],String text)\n     .types(int... keyCodes)\n     .typeUnsurely([int x,int y],String text)\n     .unstash()\n     .waitForIdle()\n     .wheel(int amount)\n     .set*(*);\n```\n\nUnchainable methods:\n\n```Java\nbuddy.printScreen()\nbuddy.printScreen(Rectangle screenRect);\nbuddy.printScreen(int width,int height);\nbuddy.printScreen(int x,int y,int width,int height);\nbuddy.getPixel(Point coords);\nbuddy.getPixel(int x,int y);\nbuddy.getScreenHeight();\nbuddy.getScreenSize();\nbuddy.getScreenWidth();\nbuddy.get*(*);\n```\n\nA Safe Mode has been added for convenience. If the user ever moves their mouse, then **UserIsActiveException** will be thrown. After each operation, it just checks the mouse coordinates, while updating its internal coordinates accordingly to the operations.\n\nIn addition, the pressed keys and pressed mouse buttons are stored internally if Release Mode is on (on by default), so that you can release everything currently pressed down to alleviate problems for the user when active.\n\nExample:\n\n```Java\nBotBuddy buddy = BotBuddy.builder().build();\n\nSystem.out.println(\"Get ready...\");\nbuddy.delay(2000);\n\ntry {\n  buddy.beginSafeMode()\n       .enter(1470,131,\"Mommy\")\n       .delay(2000) // Move your mouse during this time\n       .enter(1470,131,\"Daddy\")\n       .endSafeMode();\n}\ncatch(UserIsActiveException ex) {\n  // Release all keys and/or mouse buttons pressed down by the automatic operations\n  buddy.releasePressed();\n\n  // If you move your mouse, \"Daddy\" will not be executed\n  System.out.println(\"User is active! Stopping all automatic operations.\");\n}\n```\n\n`BotBuddy` also implements `AutoCloseable` so that you can use try-with-resource:\n\n```Java\n// This will automatically call buddy.releasePressed() for you\ntry(BotBuddy buddy = BotBuddy.builder().build()) {\n  // ...\n}\n```\n\nIf your program clicks into a virtual machine, you can change the OS to change the keyboard shortcut keys (e.g., paste):\n\n```Java\nbuddy.setOSFamily(OSFamily.MACOS);\n```\n\nWhen writing your own scripts, you can use these helper methods:\n\n- `BotBuddy.getCoords();`\n- `BotBuddy.getXCoord();`\n- `BotBuddy.getYCoord();`\n\nAlternatively, you can do one of the following for getting the mouse coordinates:\n\n- Linux:\n    - Install \"xdotool\" and do: `xdotool getmouselocation`\n\nSimilar projects:\n\n- Robot-Utils by Denys Shynkarenko (@Denysss) [[GitHub](https://github.com/Denysss/Robot-Utils)]\n- Automaton by Renato Athaydes (@renatoathaydes) [[GitHub](https://github.com/renatoathaydes/Automaton)]\n\n#### [BotBuddyCode](#using)\n\nA very simple scripting \"language\" interpreter for [BotBuddy](#botbuddy). It is **not** Turing complete.\n\nSee [BotBuddyCodeTest.bbc](src/test/resources/BotBuddyCodeTest.bbc) for a quick example of functionality. If you were to interpret this file dryly, then it would produce this output: [BotBuddyCodeTestOutput.txt](src/test/resources/BotBuddyCodeTestOutput.txt).\n\nThe idea was to make a very simple parser, without including the overhead of Groovy/JRuby into *Jeso*. In a future, separate project, I may add Groovy/JRuby support.\n\nIt can handle Ruby-like string literals and [heredoc](https://en.wikipedia.org/wiki/Here_document), and simple methods (no params).\n\nIt can accept the following input:\n\n- [java.io.BufferedReader](https://docs.oracle.com/javase/8/docs/api/java/io/BufferedReader.html)\n- [java.nio.file.Path](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Path.html) [use [java.nio.file.Paths.get(...)](https://docs.oracle.com/javase/8/docs/api/java/nio/file/Paths.html#get-java.lang.String-java.lang.String...-)]\n- List\u0026lt;String\u0026gt; using [com.esotericpig.jeso.io.StringListReader](#stringlistreader)\n- String using [java.io.StringReader](https://docs.oracle.com/javase/8/docs/api/java/io/StringReader.html)\n\nExample usage with a file:\n```Java\nimport com.esotericpig.jeso.botbuddy.BotBuddyCode;\n\ntry(BotBuddyCode bbc = BotBuddyCode.builder(Paths.get(\"file.txt\")).build()) {\n  // Don't execute any code, just output result of interpreting:\n  System.out.println(bbc.interpretDryRun());\n}\n```\n\nExample usage with a list of strings:\n```Java\nList\u003cString\u003e list = new LinkedList\u003c\u003e();\nlist.add(\"puts 'Hello World'\");\nlist.add(\"\");\nlist.add(\"get_coords\");\n\ntry(BotBuddyCode bbc = BotBuddyCode.builder(list).build()) {\n  // Interpret and execute code\n  bbc.interpret();\n}\n```\n\nExample of functionality:\n```Ruby\n# This is a comment\n\nputs \u003c\u003cEOS # Heredoc\n    Hello World\n  EOS # End tag can be indented\n\nputs \u003c\u003c-EOS # ltrim to min indent\n    Hello World\n  EOS\n\npaste 592 254 \u003c\u003c-EOS # Heredoc with other args\n    Hello World\n  EOS\n\n# Method names are flexible\nbegin_safe_mode\nendSafeMode\n\n# Quoted strings can also have newlines\nputs \"Hello \\\"\nWorld\\\"\"\nputs 'Hello \\'World\\''\n\n# Special quotes like Ruby, where you choose the terminator\nputs %(Hello \\) World)\nputs %^Hello \\^ World^\n\n# Define your own (user) method\n# - Cannot take in args\ndef my_method\n  get_coords\n  get_pixel 1839 894\n  printscreen # Saves file to current directory\n  getOSFamily\nend\n\n# Can call multiple methods in one line\ncall my_method myMethod\n```\n\nReal world example:\n```Ruby\nputs \"Get ready...\"\ndelay 2000\n\nbegin_safe_mode\n\npaste 1187 492  \"Sakana\"\npaste 1450 511  \"Fish\"\nclick 1851 1021\ndelay_long\n\npaste 1187 492  \"Niku\"\npaste 1450 511  \"Meat\"\nclick 1851 1021\n\nend_safe_mode\n```\n\n#### [BotBuddyCodeApp](#using)\n\nA simple CLI app for [BotBuddyCode](#botbuddycode) that can take in a file or read piped-in input (pipeline).\n\nPrint help:\n\n- `java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp --help`\n\nHelp:\n\n```Makefile\nUsage: BotBuddyCodeApp [options] \u003cfile\u003e [options]\n\nInterprets the contents of \u003cfile\u003e using BotBuddyCode.\nData can also be piped in, without using a file.\n\nOptions:\n    -n, --dry-run            Do not execute any code, only output the interpretation\n    ---\n    -h, --help               Print this help\n\nExamples:\n    BotBuddyCodeApp -n mydir/myfile.bbc\n    BotBuddyCodeApp 'My Dir/My File.bbc'\n    echo 'get_coords' | BotBuddyCodeApp\n```\n\nExamples:\n\n```Console\n$ java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp file.txt\n$ java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp -n file.txt\n$ echo 'get_pixel 100 100' | java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp\n$ echo 'get_pixel 100 100' | java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp -n\n```\n\n### [IO Package](#using)\n\n#### [StringListReader](#using)\n\nA [java.io.Reader](https://docs.oracle.com/javase/8/docs/api/java/io/Reader.html) for a List of Strings.\n\nThis was specifically made for [BotBuddyCode](#botbuddycode), but can be used wherever a Reader is.\n\nFor each new String in a List, it will produce a newline (`\\n`).\n\nExample usage:\n\n```Java\nimport com.esotericpig.jeso.io.StringListReader;\n\nList\u003cString\u003e list = new LinkedList\u003c\u003e();\nlist.add(\"name = ' hello World '\");\nlist.add(\"name.strip!\");\nlist.add(\"name.capitalize!\");\nlist.add(\"\");\nlist.add(\"puts name\");\n\ntry(BufferedReader lin = new BufferedReader(new StringListReader(list))) {\n  String line = null;\n\n  while((line = lin.readLine()) != null) {\n    System.out.println(line);\n  }\n}\n```\n\nAlso see:\n\n- [StringListReaderTest](src/test/java/com/esotericpig/jeso/io/StringListReaderTest.java)\n- [BotBuddyCode.Builder.input(List\u0026lt;String\u0026gt; strList)](src/main/java/com/esotericpig/jeso/botbuddy/BotBuddyCode.java)\n\n## [Hacking](#contents)\n\nFor Windows, use **./gradlew.bat** instead.\n\n```\n$ git clone 'https://github.com/esotericpig/jeso.git'\n$ cd jeso\n$ ./gradlew tasks\n\n$ ./gradlew check\n$ ./gradlew test\n\n$ ./gradlew build\n$ ./gradlew buildRelease\n$ ./gradlew buildFatRelease\n\n$ ./gradlew publish\n\n$ ./gradlew javadocZip\n$ ./gradlew sourcesJar\n\n$ ./gradlew checkGradleW\n$ ./gradlew wgetGradleWSums\n\n$ ./gradlew rsyncToGhp\n```\n\nPublishing:\n- Replace all instances of the old version number in `build.gradle` \u0026 `README.md`.\n- `./gradlew clean buildRelease buildFatRelease`\n- `gh release create v0.0.0 build/libs/jeso-*.jar build/distributions/jeso-*.zip`\n  - Replace `v0.0.0` with the new version.\n- `git fetch \u0026\u0026 git pull`\n- Build it on [JitPack](https://jitpack.io/#esotericpig/jeso).\n- `GITHUB_ACTOR=esotericpig GITHUB_TOKEN=\u003ctoken\u003e ./gradlew publish`\n- `./gradlew rsyncToGhp`\n\n## [License](#contents)\n[GNU LGPL v3+](LICENSE)\n\n\u003e Jeso ([https://github.com/esotericpig/jeso](https://github.com/esotericpig/jeso))  \n\u003e Copyright (c) 2019-2022 Jonathan Bradley Whited  \n\u003e \n\u003e Jeso is free software: you can redistribute it and/or modify  \n\u003e it under the terms of the GNU Lesser General Public License as published by  \n\u003e the Free Software Foundation, either version 3 of the License, or  \n\u003e (at your option) any later version.  \n\u003e \n\u003e Jeso is distributed in the hope that it will be useful,  \n\u003e but WITHOUT ANY WARRANTY; without even the implied warranty of  \n\u003e MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  \n\u003e GNU Lesser General Public License for more details.  \n\u003e \n\u003e You should have received a copy of the GNU Lesser General Public License  \n\u003e along with Jeso. If not, see \u003chttp://www.gnu.org/licenses/\u003e.  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesotericpig%2Fjeso","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fesotericpig%2Fjeso","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fesotericpig%2Fjeso/lists"}