Java utils to make Java less verbose and more fun.
Inspired largely by Ruby.
Name = Java + gesso + esoteric.
- Java 8 or later
Pick your poison...
You can view the packages for this project here.
You can either use Gradle or Maven.
It takes a bit more setup, but worth it since you can use any GitHub library/package afterwards.
See here for more information.
If you don't want to use your GitHub password (recommended), first create a token with at least the read:packages scope.
In ~/.gradle/gradle.properties:
gpr.user=username # Your GitHub username
gpr.key=token # Your GitHub token (or password)
In your project's build.gradle:
repositories {
maven {
name = 'Jeso GitHub Package'
url = uri('https://maven.pkg.github.com/esotericpig/jeso')
credentials {
username = project.findProperty('gpr.user') ?: System.getenv("USERNAME")
password = project.findProperty('gpr.key') ?: System.getenv("PASSWORD")
}
}
}
dependencies {
// TODO: Edit the version appropriately!
implementation 'com.esotericpig.jeso:jeso:X.X.X'
}
See here for more information.
If you don't want to use your GitHub password (recommended), first create a token with at least the read:packages scope.
In ~/.m2/settings.xml:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<activeProfiles>
<activeProfile>github</activeProfile>
</activeProfiles>
<profiles>
<profile>
<id>github</id>
<repositories>
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository>
<repository>
<id>github</id>
<name>Jeso GitHub Package</name>
<url>https://maven.pkg.github.com/esotericpig/jeso</url>
</repository>
</repositories>
</profile>
</profiles>
<servers>
<server>
<id>github</id>
<!-- TODO: Your GitHub username -->
<username>USERNAME</username>
<!-- TODO: Your GitHub token (or password) -->
<password>TOKEN</password>
</server>
</servers>
</settings>
In your project's pom.xml:
<dependencies>
<dependency>
<groupId>com.esotericpig.jeso</groupId>
<artifactId>jeso</artifactId>
<!-- TODO: Edit the version appropriately! -->
<version>X.X.X</version>
</dependency>
</dependencies>
Install the package*:
$ mvn install
*If you have bad internet, you'll need to call this multiple times until it downloads.
Download the Assets from the latest Release.
Then import the following files into your project:
Asset Files |
---|
jeso-x.x.x.jar |
jeso-x.x.x-sources.jar |
jeso-x.x.x-javadoc.zip |
Alternatively, you can just import this one file, but it also includes dependent jars (if any):
Asset Files |
---|
jeso-x.x.x-all.jar |
To build a pre-release, please do the following:
$ git clone 'https://github.com/esotericpig/jeso.git'
$ cd jeso
$ ./gradlew(.bat) clean buildRelease -x check -x test
You can probably safely exclude check and test (like in the above example) to build it faster (i.e., to not download & install development/test dependencies), as those checks should have already been run when committing the code.
Then import the following files into your project:
Release Files |
---|
build/libs/jeso-x.x.x.jar |
build/libs/jeso-x.x.x-sources.jar |
build/distributions/jeso-x.x.x-javadoc.zip |
Alternatively, you can build everything into one "fat" jar, which includes dependent jars (if any):
$ ./gradlew(.bat) clean buildFatRelease -x check -x test
Release Files |
---|
build/libs/jeso-x.x.x-all.jar |
Class | Summary | Javadoc | File |
---|---|---|---|
Arys | Utility class for Arrays | Arys.html | Arys.java |
Bools | Utility class for Booleans | Bools.html | Bools.java |
Duplicable | Generic replacement for Cloneable/clone() | Duplicable.html | Duplicable.java |
OSFamily | Enum for guessing the OS family from a String | OSFamily.html | OSFamily.java |
Strs | Utility class for Strings | Strs.html | Strs.java |
Sys | Utility class for System | Sys.html | Sys.java |
Class | Summary | Javadoc | File |
---|---|---|---|
BotBuddy | Wrapper around java.awt.Robot | BotBuddy.html | BotBuddy.java |
BotBuddy.Shortcut | Functional interface for automatic operations for BotBuddy | BotBuddy.Shortcut.html | BotBuddy.java#Shortcut |
BotBuddyCode | Very simple scripting "language" interpreter for BotBuddy | BotBuddyCode.html | BotBuddyCode.java |
BotBuddyCodeApp | Simple CLI app for BotBuddyCode that can take in a file or read piped-in input (pipeline) | BotBuddyCodeApp.html | BotBuddyCodeApp.java |
Class | Summary | Javadoc | File |
---|---|---|---|
LineOfCode | Immutable class that stores a Line Number and Line Column | LineOfCode.html | LineOfCode.java |
ParseCodeException | Runtime Exception that can store a LineOfCode and build a detailed message with it | ParseCodeException.html | ParseCodeException.java |
Class | Summary | Javadoc | File |
---|---|---|---|
StringListReader | Reader for a list of Strings | StringListReader.html | StringListReader.java |
A utility class for Arrays.
import com.esotericpig.jeso.Arys;
String[] breakfast = {"coffee","coffee",null,"eggs","eggs",null,"toast","turkey sausage"};
String[] newArray = null;
Random rand = new Random();
// Remove nulls; varargs
// - [coffee, coffee, eggs, eggs, toast, turkey sausage]
println( Arrays.toString(Arys.compact(breakfast)) );
// Move nulls to end; mutable
// - [coffee, coffee, eggs, eggs, toast, turkey sausage, null, null]
println( Arrays.toString(Arys.compactMut(breakfast)) );
// Join into a String; varargs
// - 123
println( Arys.join(1,2,3) );
// Join into a String with a custom separator; varargs
// - coffee,coffee,eggs,eggs,toast,turkey sausage,null,null
// - coffee | coffee | eggs | eggs | toast | turkey sausage | null | null
println( Arys.joins(',',breakfast) );
println( Arys.joins(" | ",breakfast) );
// Create a new array using Reflection
// - [Ljava.lang.String;@][3]
newArray = Arys.newArray(breakfast,3);
println( newArray + "][" + newArray.length + "]" );
// Get a random element; varargs
// - eggs
// - coffee
println( Arys.sample(breakfast) );
println( Arys.sample(rand,breakfast) );
// Get multiple random elements using a shuffle strategy (don't repeat); varargs
// - [eggs, null, coffee]
// - [coffee, null, turkey sausage, coffee, null, eggs, eggs, toast]
// - [coffee, turkey sausage, eggs]
println( Arrays.toString(Arys.samples(3,breakfast)) );
println( Arrays.toString(Arys.samples(100,breakfast)) );
println( Arrays.toString(Arys.samples(3,rand,breakfast)) );
// Remove duplicate elements; varargs
// - [coffee, eggs, toast, turkey sausage, null]
println( Arrays.toString(Arys.unique(breakfast)) );
// Remove duplicate elements (pad with nulls); mutable
// - [coffee, eggs, toast, turkey sausage, null, null, null, null]
println( Arrays.toString(Arys.uniqueMut(breakfast)) );
// Create a new array from a List using Reflection
// - [Ljava.lang.String;@][8]
newArray = Arys.toArray(breakfast,Arrays.asList(breakfast));
println( newArray + "][" + newArray.length + "]" );
A utility class for Booleans.
import com.esotericpig.jeso.Bools;
// ["1","on","t","true","y","yes"] are all true and case-insensitive
Bools.parse("On"); // true
A Generic replacement for Cloneable/clone().
Almost every Library has their own, so let's reinvent the wheel.
Java Cloning: Even Copy Constructors Are Not Enough [DZone]
CopyConstructorExample.java [GitHub]
import com.esotericpig.jeso.Duplicable;
public class Testbed
public static void main(String[] args) {
Alumnus alum1 = new Alumnus("Bob","MySchool","MyJob");
Alumnus alum2 = alum1.dup();
// Same school
alum2.name = "Fred";
alum2.job = "CoolJob";
System.out.println(alum1);
System.out.println(alum2);
}
}
class User implements Duplicable<User> {
public String name;
public User(String name) { this.name = name; }
protected User(User user) { this.name = user.name; }
public User dup() { return new User(this); }
}
class Student extends User {
public String school;
public Student(String name,String school) { super(name); this.school = school; }
protected Student(Student student) { super(student); this.school = student.school; }
@Override
public Student dup() { return new Student(this); }
}
class Alumnus extends Student {
public String job;
public Alumnus(String name,String school,String job) { super(name,school); this.job = job; }
protected Alumnus(Alumnus alumnus) { super(alumnus); this.job = alumnus.job; }
@Override
public Alumnus dup() { return new Alumnus(this); }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name).append(":\t").append('[');
sb.append(school).append(',');
sb.append(job).append(']');
return sb.toString();
}
}
An enum for guessing the OS family from a String.
Most users will only need Sys.OS_FAMILY.
import com.esotericpig.jeso.OSFamily;
Random rand = new Random();
// Used for testing
println( OSFamily.getRandValue(rand) );
// - OSFamily.LINUX
// - OSFamily.MACOS
// - OSFamily.WINDOWS
// - OSFamily.UNKNOWN
println( OSFamily.guessFromName("GNU/Linux Fedora") );
println( OSFamily.guessFromName("Mac OS X") );
println( OSFamily.guessFromName("Microsoft Windows XP") );
println( OSFamily.guessFromName("TempleOS") );
A utility class for Strings.
import com.esotericpig.jeso.Strs;
// Remove (left) leading whitespace; mutable
// - 'Hello World '
println( "'" + Strs.ltrim(new StringBuilder(" Hello World ")) + "'" );
// Remove (right) trailing whitespace; mutable
// - ' Hello World'
println( "'" + Strs.rtrim(new StringBuilder(" Hello World ")) + "'" );
// Remove leading & trailing whitespace; mutable
// - 'Hello World'
println( "'" + Strs.trim(new StringBuilder(" Hello World ")) + "'" );
A utility class for System.
import com.esotericpig.jeso.Sys;
// "Unknown" if not set
println( Sys.OS_NAME );
// Uses the OSFamily enum
println( Sys.OS_FAMILY );
// Gets a System property and ignores SecurityException if thrown
// (will return the specified default value or null)
// - If you use System.getProperty(...), then it can potentially throw a SecurityException, but
// you might want your app to continue to work even if "os.name" is blocked from being read
println( Sys.getSafeProp("os.name") ); // If not set, null is the default value
println( Sys.getSafeProp("os.name","Unknown") );
A wrapper around java.awt.Robot.
Warning for Linux users:
- On Wayland, Java's Robot will not work. You will need to either use X11 or XWayland, until either OpenJDK or Wayland is fixed.
See the samples for a quick peek.
It can be used for...
- Moving the mouse and pasting in text.
- Automating tedious tasks that a website/program has not implemented yet, such as inputting a CSV file into a website, row by row.
- 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.
- Testing/QAing your desktop applications.
Example usage:
import com.esotericpig.jeso.botbuddy.BotBuddy;
// ...class...main...
BotBuddy buddy = BotBuddy.builder().build();
buddy.paste(999,493,"Fish")
.enter(1427,500,"Sakana");
buddy.click(1853,1015)
.delayLong();
To construct a class, the Builder Design Pattern is used:
BotBuddy buddy = BotBuddy.builder()
.autoDelay(false)
.fastDelay(33)
.build();
Most methods can also be chained together:
buddy.beep()
.beginFastMode()
.beginSafeMode()
.clearPressed()
.clearPressedButtons()
.clearPressedKeys()
.click([int button])
.click([int x,int y,int button])
.clicks(int... buttons)
.copy(String text,[ClipboardOwner owner])
.delay(int delay)
.delayAuto()
.delayFast()
.delayLong()
.delayShort()
.doubleClick([int button])
.doubleClick([int x,int y,int button])
.drag(int fromX,int fromY,int toX,int toY,[int button])
.endFastMode()
.endSafeMode()
.enter([String text])
.enter([int x,int y,String text])
.leftClick([int x,int y])
.middleClick([int x,int y])
.move(int x,int y)
.paste([String text])
.paste([int x,int y,String text])
.pressButton([int x,int y],int button)
.pressButtons(int... buttons)
.pressKey([int x,int y],int keyCode)
.pressKeys(int... keyCodes)
.releaseButton([int x,int y],int button)
.releaseButtons([int... buttons])
.releaseKey([int x,int y],int keyCode)
.releaseKeys([int... keyCodes])
.releasePressed()
.rightClick([int x,int y])
.rollButtons(int... buttons)
.rollKeys(int... keyCodes)
.shortcut(BotBuddy.Shortcut shortcut)
.stash()
.type([int x,int y],int keyCode)
.type([int x,int y],String text)
.types(int... keyCodes)
.typeUnsurely([int x,int y],String text)
.unstash()
.waitForIdle()
.wheel(int amount)
.set*(*);
Unchainable methods:
buddy.printScreen()
buddy.printScreen(Rectangle screenRect);
buddy.printScreen(int width,int height);
buddy.printScreen(int x,int y,int width,int height);
buddy.getPixel(Point coords);
buddy.getPixel(int x,int y);
buddy.getScreenHeight();
buddy.getScreenSize();
buddy.getScreenWidth();
buddy.get*(*);
A 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.
In 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.
Example:
BotBuddy buddy = BotBuddy.builder().build();
System.out.println("Get ready...");
buddy.delay(2000);
try {
buddy.beginSafeMode()
.enter(1470,131,"Mommy")
.delay(2000) // Move your mouse during this time
.enter(1470,131,"Daddy")
.endSafeMode();
}
catch(UserIsActiveException ex) {
// Release all keys and/or mouse buttons pressed down by the automatic operations
buddy.releasePressed();
// If you move your mouse, "Daddy" will not be executed
System.out.println("User is active! Stopping all automatic operations.");
}
BotBuddy
also implements AutoCloseable
so that you can use try-with-resource:
// This will automatically call buddy.releasePressed() for you
try(BotBuddy buddy = BotBuddy.builder().build()) {
// ...
}
If your program clicks into a virtual machine, you can change the OS to change the keyboard shortcut keys (e.g., paste):
buddy.setOSFamily(OSFamily.MACOS);
When writing your own scripts, you can use these helper methods:
BotBuddy.getCoords();
BotBuddy.getXCoord();
BotBuddy.getYCoord();
Alternatively, you can do one of the following for getting the mouse coordinates:
- Linux:
- Install "xdotool" and do:
xdotool getmouselocation
- Install "xdotool" and do:
Similar projects:
- Robot-Utils by Denys Shynkarenko (@Denysss) [GitHub]
- Automaton by Renato Athaydes (@renatoathaydes) [GitHub]
A very simple scripting "language" interpreter for BotBuddy. It is not Turing complete.
See BotBuddyCodeTest.bbc for a quick example of functionality. If you were to interpret this file dryly, then it would produce this output: BotBuddyCodeTestOutput.txt.
The 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.
It can handle Ruby-like string literals and heredoc, and simple methods (no params).
It can accept the following input:
- java.io.BufferedReader
- java.nio.file.Path [use java.nio.file.Paths.get(...)]
- List<String> using com.esotericpig.jeso.io.StringListReader
- String using java.io.StringReader
Example usage with a file:
import com.esotericpig.jeso.botbuddy.BotBuddyCode;
try(BotBuddyCode bbc = BotBuddyCode.builder(Paths.get("file.txt")).build()) {
// Don't execute any code, just output result of interpreting:
System.out.println(bbc.interpretDryRun());
}
Example usage with a list of strings:
List<String> list = new LinkedList<>();
list.add("puts 'Hello World'");
list.add("");
list.add("get_coords");
try(BotBuddyCode bbc = BotBuddyCode.builder(list).build()) {
// Interpret and execute code
bbc.interpret();
}
Example of functionality:
# This is a comment
puts <<EOS # Heredoc
Hello World
EOS # End tag can be indented
puts <<-EOS # ltrim to min indent
Hello World
EOS
paste 592 254 <<-EOS # Heredoc with other args
Hello World
EOS
# Method names are flexible
begin_safe_mode
endSafeMode
# Quoted strings can also have newlines
puts "Hello \"
World\""
puts 'Hello \'World\''
# Special quotes like Ruby, where you choose the terminator
puts %(Hello \) World)
puts %^Hello \^ World^
# Define your own (user) method
# - Cannot take in args
def my_method
get_coords
get_pixel 1839 894
printscreen # Saves file to current directory
getOSFamily
end
# Can call multiple methods in one line
call my_method myMethod
Real world example:
puts "Get ready..."
delay 2000
begin_safe_mode
paste 1187 492 "Sakana"
paste 1450 511 "Fish"
click 1851 1021
delay_long
paste 1187 492 "Niku"
paste 1450 511 "Meat"
click 1851 1021
end_safe_mode
A simple CLI app for BotBuddyCode that can take in a file or read piped-in input (pipeline).
Print help:
java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp --help
Help:
Usage: BotBuddyCodeApp [options] <file> [options]
Interprets the contents of <file> using BotBuddyCode.
Data can also be piped in, without using a file.
Options:
-n, --dry-run Do not execute any code, only output the interpretation
---
-h, --help Print this help
Examples:
BotBuddyCodeApp -n mydir/myfile.bbc
BotBuddyCodeApp 'My Dir/My File.bbc'
echo 'get_coords' | BotBuddyCodeApp
Examples:
$ java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp file.txt
$ java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp -n file.txt
$ echo 'get_pixel 100 100' | java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp
$ echo 'get_pixel 100 100' | java -cp 'build/libs/*' com.esotericpig.jeso.botbuddy.BotBuddyCodeApp -n
A java.io.Reader for a List of Strings.
This was specifically made for BotBuddyCode, but can be used wherever a Reader is.
For each new String in a List, it will produce a newline (\n
).
Example usage:
import com.esotericpig.jeso.io.StringListReader;
List<String> list = new LinkedList<>();
list.add("name = ' hello World '");
list.add("name.strip!");
list.add("name.capitalize!");
list.add("");
list.add("puts name");
try(BufferedReader lin = new BufferedReader(new StringListReader(list))) {
String line = null;
while((line = lin.readLine()) != null) {
System.out.println(line);
}
}
Also see:
For Windows, use ./gradlew.bat instead.
$ git clone 'https://github.com/esotericpig/jeso.git'
$ cd jeso
$ ./gradlew tasks
$ ./gradlew check
$ ./gradlew test
$ ./gradlew build
$ ./gradlew buildRelease
$ ./gradlew buildFatRelease
$ ./gradlew publish
$ ./gradlew javadocZip
$ ./gradlew sourcesJar
$ ./gradlew checkGradleW
$ ./gradlew wgetGradleWSums
$ ./gradlew rsyncToGhp
Jeso (https://github.com/esotericpig/jeso)
Copyright (c) 2019-2022 Jonathan Bradley WhitedJeso is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.Jeso is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public License
along with Jeso. If not, see http://www.gnu.org/licenses/.