diff --git a/lib/compile/jar/nailgun-0.7.1.jar b/lib/compile/jar/nailgun-0.7.1.jar new file mode 100644 index 0000000..05cbbaf Binary files /dev/null and b/lib/compile/jar/nailgun-0.7.1.jar differ diff --git a/lib/runtime/jar/nailgun-0.7.1.jar b/lib/runtime/jar/nailgun-0.7.1.jar new file mode 100644 index 0000000..05cbbaf Binary files /dev/null and b/lib/runtime/jar/nailgun-0.7.1.jar differ diff --git a/project.properties b/project.properties index 844e923..a3c9152 100644 --- a/project.properties +++ b/project.properties @@ -1,7 +1,7 @@ -#Sat, 06 Nov 2010 10:35:28 -0500 +#Sat, 06 Nov 2010 16:20:53 -0500 lib.local=true name=groovy-twitter-cli version=0.1 -build.number=19 +build.number=60 nailgun.classpath.dir=/home/jdbernard/programs/nailgun/classpath win32.nailgun.classpath.dir=C\:/Documents and Settings/jbernard/My Documents/ng-classpath diff --git a/src/com/jdbernard/twitter/ConsoleColor.java b/src/com/jdbernard/twitter/ConsoleColor.java new file mode 100644 index 0000000..26c033c --- /dev/null +++ b/src/com/jdbernard/twitter/ConsoleColor.java @@ -0,0 +1,58 @@ +package com.jdbernard.twitter; + +public class ConsoleColor { + + public final Colors fg; + public final Colors bg; + public final boolean bright; + + public static enum Colors { + BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE } + + public ConsoleColor(String propString) { + String[] vals = propString.split("[,;: ]"); + + fg = Colors.valueOf(vals[0]); + + if (vals.length == 2) { + bg = null; + bright = Boolean.parseBoolean(vals[1]); + } else if (vals.length == 3) { + bg = Colors.valueOf(vals[1]); + bright = Boolean.parseBoolean(vals[2]); + } else { bg = null; bright = false; } + } + + public ConsoleColor(Colors fgColor) { this(fgColor, Colors.BLACK, false); } + + public ConsoleColor(Colors fgColor, boolean bright) { + this(fgColor, Colors.BLACK, bright); + } + + public ConsoleColor(Colors fgColor, Colors bgColor, boolean bright) { + this.fg = fgColor; this.bg = bgColor; this.bright = bright; + } + + public String toString() { + String result = "\u001b["; + boolean needSemi = false; + + if (bright) { + result += "1"; + needSemi = true; + } + + if (fg != null) { + if (needSemi) result += ";"; + result += "3" + Integer.toString(fg.ordinal()); + needSemi = true; + } + + if (bg != null) { + if (needSemi) result += ";"; + result += "4" + Integer.toString(bg.ordinal()); + } + + return result + "m"; + } +} diff --git a/src/com/jdbernard/twitter/TwitterCLI.groovy b/src/com/jdbernard/twitter/TwitterCLI.groovy index fe9afe3..0c1b43a 100644 --- a/src/com/jdbernard/twitter/TwitterCLI.groovy +++ b/src/com/jdbernard/twitter/TwitterCLI.groovy @@ -1,5 +1,6 @@ package com.jdbernard.twitter +import com.martiansoftware.nailgun.NGContext import twitter4j.Twitter import twitter4j.TwitterFactory import twitter4j.conf.Configuration @@ -7,102 +8,235 @@ import twitter4j.conf.PropertyConfiguration public class TwitterCLI { + private static String EOL = System.getProperty("line.separator") + private static TwitterCLI nailgunInst private Twitter twitter + private Scanner stdin + + private Map colors = [:] + + private int terminalWidth + private boolean colored + public static void main(String[] args) { TwitterCLI inst = new TwitterCLI(new File(System.getProperty("user.home"), ".groovy-twitter-cli-rc")) - inst.run(args as List) + inst.run((args as List) as Queue) } - public static void nailMain(String[] args) { + public static void nailMain(NGContext context) { + if (nailgunInst == null) + nailgunInst = new TwitterCLI(new File( + System.getProperty("user.home"), ".groovy-twitter-cli-rc")) + else + nailgunInst.stdin = new Scanner(context.in) - if (nailgunInst == null) nailgunInst = new TwitterCLI(new File( - System.getProperty("user.home"), ".groovy-twitter-cli-rc")) - - nailgunInst.run(args as List) + nailgunInst. + nailgunInst.run((context.args as List) as Queue) } - public static void setColor(boolean bright, int code) { - print "\u001b[${bright?'1':'0'};${code}m" + public static void reconfigure(Queue args) { + if (nailgunInst == null) main(args as String[]) + else { + nailgunInst = null + nailgunInst = new TwitterCLI(new File( + System.getProperty("user.home"), ".groovy-twitter-cli-rc")) + + nailgunInst.run(args) + } } - public static void resetColor() { print "\u001b[m" } + static String wrapToWidth(String text, int width, String prefix, String suffix) { + int lastSpaceIdx = 0; + int curLineLength = 0; + int lineStartIdx = 0; + int i = 0; + int actualWidth = width - prefix.length() - suffix.length() + String wrapped = "" - public static void colorPrint(def message, boolean bright, int color) { - setColor(bright, color) - print message - resetColor() + text = text.replaceAll("[\n\r]", " ") + + for (i = 0; i < text.length(); i++) { + + curLineLength++ + if (curLineLength > actualWidth) { + wrapped += prefix + text[lineStartIdx.. 1) + wrapped += prefix + text[lineStartIdx.. conf = new PropertyConfiguration(is) } + Properties cfg = new Properties() + propFile.withInputStream { is -> cfg.load(is) } // create a twitter instance - twitter = (new TwitterFactory(conf)).getInstance() + twitter = (new TwitterFactory(new PropertyConfiguration(cfg))).getInstance() + // configure the colors + colors.author = new ConsoleColor(cfg.getProperty("colors.author", "BLUE:false")) + colors.mentioned = new ConsoleColor(cfg.getProperty("colors.mentioned", "GREEN:false")) + colors.error = new ConsoleColor(cfg.getProperty("colors.error", "RED:true")) + colors.option = new ConsoleColor(cfg.getProperty("colors.option", "YELLOW:true")) + colors.even = new ConsoleColor(cfg.getProperty("colors.even", "WHITE")) + colors.odd = new ConsoleColor(cfg.getProperty("colors.odd", "YELLOW")) + + // configure the terminal width + terminalWidth = (System.getenv().COLUMNS ?: cfg.terminalWidth ?: 79) as int + + colored = (cfg.colored ?: 'true') as boolean + + stdin = new Scanner(System.in) } - public void run(List args) { + public void run(Queue args) { if (args.size() < 1) printUsage() - switch (args[0].toLowerCase()) { - case ~/t.*/: timeline(args.tail()); break - case ~/p.*/: post(args.tail()); break + while (args.peek()) { + def command = args.poll() - default: - printUsage() - } + switch (command.toLowerCase()) { + case ~/h.*/: help(args); break + case ~/p.*/: post(args); break + case ~/r.*/: reconfigure(args); break + case ~/se.*/: set(args); break + case ~/sh.*/: show(args); break + case ~/t.*/: timeline(args); break - } - - public void timeline(List args) { - - // default to showing my friends timeline - if (args.size() == 0) args = ["friends"] - - while (args.size() > 0) { - def option = args.pop() - switch (option) { - // friends - case ~/f.*/: printTimeline(twitter.friendsTimeline); break - // mine - case ~/m.*/: printTimeline(twitter.userTimeline); break - // user - case ~/u.*/: - if (args.size() == 0) - colorPrintln("No user specificied.", true, 31) - else printTimeline(twitter.getUserTimeline(args.pop())) - break; default: - colorPrint("Unknown timeline: ", true, 31) - colorPrintln(option, true, 33) - break; + printUsage() } } } + public void help(Queue args) { + + } + + public void post(Queue args) { + def status = args.poll() + + if (!status) { + println color("post ", colors.option) + + color("command requires one option: ", colors.error) + + "twitter post " + return + } + + if (status.length() > 140) { + println color("Status exceeds Twitter's 140 character limit.", colors.error) + return + } + + print "Update status: '$status'? " + if (stdin.nextLine() ==~ /yes|y|true|t/) + twitter.updateStatus(status) + } + + public void set(Queue args) { + def option = args.poll() + def value = args.poll() + + if (!value) { // note: if option is null, value is null + println color("set", colors.option) + + color(" command requires two options: ", colors.error) + + "twitter set " + return + } + + switch (option) { + case "terminalWidth": terminalWidth = value as int; break + case "colored": colored = value.toLowerCase() ==~ /true|t|on|yes|y/ + break + + default: + println color("No property named ", colors.error) + + color(option, colors.option) + + color(" exists.", colors.error) + } + } + + public void show(Queue args) { + + } + + public void timeline(Queue args) { + + String timeline = args.poll() ?: "friends" + + switch (timeline) { + // friends + case ~/f.*/: printTimeline(twitter.friendsTimeline); break + // mine + case ~/m.*/: printTimeline(twitter.userTimeline); break + // user + case ~/u.*/: + String user = args.poll() + if (user) { + if (user.isNumber()) + printTimeline(twitter.getUserTimeline(user as int)) + else printTimeline(twitter.getUserTimeline(user)) + } else println color("No user specified.", colors.error) + break; + default: + println color("Unknown timeline: ", colors.error) + + color(timeline, colors.option) + break; + } + } + void printTimeline(def timeline) { + + int authorLen = 0, textLen + String statusIndent + timeline.each { status -> - colorPrint(status.user.screenName, true, 34) + if (status.user.screenName.length() > authorLen) + authorLen = status.user.screenName.length() + } + + timeline.eachWithIndex { status, rowNum -> + String text = status.text + print color(status.user.screenName.padLeft(authorLen), colors.author) print ": " - println status.text - println "" + statusIndent = "".padLeft(authorLen + 2) + textLen = terminalWidth - statusIndent.length() + + if (text.length() > textLen) { + text = wrapToWidth(text, terminalWidth, statusIndent, ""). + substring(statusIndent.length()) + } + + text = text.replaceAll(/(@\w+)/, color("\$1", colors.mentioned)) + println color(text, (rowNum % 2 == 0 ? colors.even : colors.odd)) } } public static void printUsage() { // TODO } + + public String resetColor() { colored ? "\u001b[m" : "" } + + public String color(def message, ConsoleColor color) { + if (!colored) return message + return color.toString() + message + resetColor() + } + }