package com.macetech import java.net.URL import java.util.regex.Pattern import java.util.regex.Matcher import groovy.util.slurpersupport.GPathResult import java.text.SimpleDateFormat public class TwitterTable { static colorMap = ["black": "000000", "red": "ff0000", "green": "00ff00", "blue": "0000ff", "yellow": "ffff00", "cyan": "00ffff", "magenta": "ff00ff", "gray": "c0c0c0", "grey": "c0c0c0", "white": "ffffff"] static godAuthors = ["jdmounge (Jason Moungey)", "Macetech (macetech LLC)", "macegr (Garrett Mace)"] static Pattern pixelPattern = Pattern.compile(".*?([0-8]),([0-8]),([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2}).*?", Pattern.MULTILINE | Pattern.DOTALL); static int SEARCH_SLEEP_SECONDS = 30 boolean serialTestMode = false SerialWriter serialWriter = null def processedEntries = [] // list of IDs File logFile = null public static void main(String[] args) { /* - allow people with authority to send clear command - figure out timing for replaying tweets if no request are coming in - write the information out as XML so the same parsing algorithm can be used */ TwitterTable ts = new TwitterTable() ts.serialTestMode = args && args[0] != null && args[0] == "test" if (ts.init()) { ts.run() ts.close() } } boolean init() { if (serialTestMode) { println "Running serial connection in test mode (skipping all commands)" } else { serialWriter = new SerialWriter() if (!serialWriter.init("/dev/tty.EPBMX-COM-DevB-1")) { println "Could not connect to serial port - quitting" return false } println "Succesfully connected to serial port" serialWriter.writePacket(SerialWriter.COMMAND_FULL_CLEAR) } String fileName = "twitter-table-${new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date())}.log" logFile = new File(fileName) logFile.withWriterAppend { it.append("\n") } return true } def close() { if (!serialTestMode) { serialWriter.close(); } logFile.withWriterAppend { it.append("\n") } } def run() { while (true) { print "\nChecking for new tweets..." def responseText = new URL("http://search.twitter.com/search.atom?q=%23ledtable&rpp=100").text println "complete" //println("${responseText}\n") GPathResult entries = new XmlSlurper().parseText(responseText).entry println "Retrieved ${entries.size()} tweets" entries.iterator().reverse().each { entry -> processEntry(entry) } printRateLimitStatus() Thread.sleep(SEARCH_SLEEP_SECONDS * 1000) } } def processEntry(GPathResult entry) { // only process entries that have not been processed if (!processedEntries.contains(entry.id)) { processedEntries << entry.id //println("parsing: ${entry.title}\n") println(["", entry.published, entry.author.name].join("\n")) // write log file in format that can use the same parser logFile.withWriterAppend { it.append("\n") it.append(" ${entry.id}\n") it.append(" ${entry.published}\n") it.append(" \n") it.append(" ${entry.author.name}\n") it.append(" ${entry.author.uri}\n") it.append(" \n") it.append(" ") } // convert color words to hex String post = entry.title colorMap.each { key, value -> post = post.replace(key, value) } if (post.contains("clear") && godAuthors.contains(entry.author.name)) { if (!serialTestMode) { serialWriter.writePacket(SerialWriter.COMMAND_FULL_CLEAR) } println("clear"); logFile.withWriterAppend { it.append("clear") } } else { // only keep valid characters; max color value is FE to maintain protocol post.toUpperCase().replaceAll("[^0-9A-F,;]", "").replaceAll("FF", "FE").split(";").each { Matcher m = pixelPattern.matcher(it); if (m.matches() && m.groupCount() == 5) { //println "matched: ${m.group(1)},${m.group(2)},${m.group(3)} ${m.group(4)} ${m.group(5)}" byte x = Integer.parseInt(m.group(1)) byte y = Integer.parseInt(m.group(2)) byte r = Integer.parseInt(m.group(3), 16) byte g = Integer.parseInt(m.group(4), 16) byte b = Integer.parseInt(m.group(5), 16) byte[] bytes = [x, y, r, g, b]; println("x,y,r,g,b=" + x + "," + y + "," + m.group(3) + "," + m.group(4) + "," + m.group(5)); logFile.withWriterAppend { it.append("${x + "," + y + "," + m.group(3) + m.group(4) + m.group(5)};") } if (!serialTestMode) { if (serialWriter.writePacketStart()) { serialWriter.writePacket(bytes); } else { println "ERROR writing packet" } serialWriter.writePacketStop(); } } } } logFile.withWriterAppend { it.append("\n") it.append("\n") } } } def printRateLimitStatus() { def xml = new XmlSlurper().parseText(new URL("http://api.twitter.com/1/account/rate_limit_status.xml").text) println "\nRemaining hits: ${xml.'remaining-hits'}" //println "Hourly limit: ${xml.'hourly-limit'}" //println "Reset time: ${xml.'reset-time'}" //println "Reset time (seconds): ${xml.'reset-time-in-seconds'}" } }