Skip to content

esotericpig/jeso

Repository files navigation

Jeso

CI Status Javadoc Source Code License

Java utils to make Java less verbose and more fun.

Inspired largely by Ruby.

Name = Java + gesso + esoteric.

Contents

  • 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

Jeso Javadoc

Top Package [Javadoc]

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

BotBuddy Package [Javadoc]

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

Code Package [Javadoc]

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

IO Package [Javadoc]

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

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:

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

GNU LGPL v3+

Jeso (https://github.com/esotericpig/jeso)
Copyright (c) 2019-2022 Jonathan Bradley Whited

Jeso 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/.