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'}"
}
}