package motherbugtweetntwitch.app;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.osgi.service.log.LogService;
import com.buglabs.application.IServiceProvider;
import com.buglabs.bug.base.pub.IBUGBaseControl;
import com.buglabs.bug.base.pub.ITimeProvider;
import com.buglabs.bug.base.pub.IToneGenerator;
import com.buglabs.bug.module.camera.pub.ICameraDevice;
import com.buglabs.bug.module.motion.pub.IMotionObserver;
import com.buglabs.bug.module.motion.pub.IMotionSubject;
import org.apache.commons.codec.binary.Base64;
import com.buglabs.device.ButtonEvent;
import com.buglabs.device.IButtonEventListener;
import com.buglabs.device.IButtonEventProvider;
import com.buglabs.services.ws.PublicWSAdmin;
/**
*
* @author jconnolly
*
* This is the main application that does most of the grunt work.
* I hope to make it more modular with the next version.
*
*/
public class MotherBUGTweetNTwitchApp implements IButtonEventListener,
IMotionObserver {
//basic config vars fill in the twitxr/twitter vars with your login info
//go to http://twitxr.com
//go to http://twitter.com
//register your account and put your registration info into the vars below
private final String TWITXR_LOGIN = " " ;
private final String TWITXR_PASSWD = " ";
private final String TWITTER_LOGIN = " ";
private final String TWITTER_PASSWD = " ";
private final boolean DEBUG = true;
private final String PATH = "/tmp/tempimage.jpeg";
//variable in seconds to prevent rapid-fire tweets/twitches.
//change this to lower # (>0) to allow for more frequent twitters about motion events
private final int MOTION_DETECT_DELAY = 3600;
//handles to services
private ICameraDevice cam;
private IButtonEventProvider buttonProv;
private LogService log;
private IMotionSubject motion;
//eventually want to use these to make it more remotely customizable
private IBUGBaseControl baseControl;
private IToneGenerator toneGen;
private PublicWSAdmin wsAdmin;
private ITimeProvider timeProv;
//press button to go into motion detection mode
private boolean detectionMode;
//press holds the value (in ms) for last time motion was detected
private long lastMotionTime;
///times to begin tweeting/twitching motion. In 24-hour format
private String startHour = "22";
private String endHour = "13";
//sets up the handles to services and registers the appropriate listeners
public MotherBUGTweetNTwitchApp(IServiceProvider serviceProv) {
baseControl = (IBUGBaseControl) serviceProv
.getService(IBUGBaseControl.class);
toneGen = (IToneGenerator) serviceProv.getService(IToneGenerator.class);
cam = (ICameraDevice) serviceProv.getService(ICameraDevice.class);
buttonProv = (IButtonEventProvider) serviceProv
.getService(IButtonEventProvider.class);
wsAdmin = (PublicWSAdmin) serviceProv.getService(PublicWSAdmin.class);
motion = (IMotionSubject) serviceProv.getService(IMotionSubject.class);
timeProv = (ITimeProvider) serviceProv.getService(ITimeProvider.class);
log = (LogService) serviceProv.getService(LogService.class);
motion.register(this);
buttonProv.addListener(this);
//Change to TRUE if you want app to start detecting motion without having to touch a button
detectionMode = true;
lastMotionTime = timeProv.getTime().getTime();
}
// wait for button event to begin detecting motion
public void buttonEvent(ButtonEvent event) {
detectionMode = true;
log.log(LogService.LOG_DEBUG, "MotherBUGTweetNTwitch: Button pressed, begin detecting motion.");
}
// checks to see if button was presSearches across multiple fields will be executed with the '&' operator sed to begin detecting motion
//
public void motionDetected() {
if (!detectionMode) {
log
.log(LogService.LOG_DEBUG,
"MotherBUGTweetNTwitch: Motion detected, but button not pressed to engaged begin waiting.");
return;
}
if ((getTimeElapsed() < this.MOTION_DETECT_DELAY)) {
log
.log(LogService.LOG_DEBUG,
"MotherBUGTweetNTwitch: Motion detected, but not enough time elapsed to do anything.");
return;
}
if (!(timeInRange())) {
log
.log(LogService.LOG_DEBUG,
"MotherBUGTweetNTwitch: Motion detected, but not in range of time to tweet and twitch");
return;
}
tweet();
writeImageToFile();
postImageToTwitxr();
}
// returns true if current hour is before (or equal to) endHour, or after (or equal to) beginHour
private boolean timeInRange() {
Date now = timeProv.getTime();
SimpleDateFormat formatter = new SimpleDateFormat("HH");
String hour = formatter.format(now);
log.log(LogService.LOG_DEBUG, "MotherBUGTweetNTwitch: The hour is " + hour + " and the start hour is "
+ startHour + " and end hour is " + endHour);
return (Integer.parseInt(hour) <= Integer.parseInt(endHour) || Integer
.parseInt(hour) >= Integer.parseInt(startHour));
}
private int getTimeElapsed() {
long now = timeProv.getTime().getTime();
int timeDiffInSeconds = (int) ((now - lastMotionTime) / 1000);
lastMotionTime = now;
log.log(log.LOG_DEBUG, "MotherBUGTweetNTwitch: Time between last motion detection and now: "
+ timeDiffInSeconds);
return timeDiffInSeconds;
}
public void tweet() {
String userPass = new String(Base64.encodeBase64((this.TWITTER_LOGIN
+ ":" + this.TWITTER_PASSWD).getBytes()));
HttpURLConnection c = null;
InputStream input;
BufferedReader dataInput;
StringBuffer buffer = null;
String line;
URL u;
try {
u = new URL("http://twitter.com/statuses/update.xml");
} catch (MalformedURLException e) {
System.err.print(e.getMessage());
log.log(LogService.LOG_ERROR, e.getMessage());
return;
}
int responseCode = -1;
try {
c = (HttpURLConnection) u.openConnection();
c.setRequestProperty("Authorization", "Basic " + userPass);
c.setDoOutput(true);
c.setRequestMethod("POST");
OutputStream out = c.getOutputStream();
// Form the POST parameters
String content = "";
content += "status="
+ URLEncoder
.encode(
"(Random key to fool twitter "
+ Math.random()
+ ") Motion detected on the "+TWITTER_LOGIN+" BUG! Check http://twitxr.com/"+TWITXR_LOGIN+" for the image",
"UTF-8");
out.write(content.toString().getBytes("UTF-8"));
out.close();
responseCode = c.getResponseCode();
} catch (IOException e) {
System.err.println(e.getMessage());
}
if (responseCode != HttpURLConnection.HTTP_OK) {
if (responseCode == 401) {
log
.log(LogService.LOG_DEBUG,
"MotherBUGTweetNTwitch: Incorrect Twitter username or password");
}
return;
}
try {
buffer = new StringBuffer();
input = c.getInputStream();
dataInput = new BufferedReader(new InputStreamReader(input));
while ((line = dataInput.readLine()) != null) {
buffer.append(line);
buffer.append('\n');
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
String response = buffer.toString();
log.log(LogService.LOG_DEBUG, "MotherBUGTweetNTwitch: Logged into Twitter as " + this.TWITTER_LOGIN);
log.log(LogService.LOG_DEBUG, "MotherBUGTweetNTwitch: Received response from Twitter server: "
+ response);
return;
}
public void postImageToTwitxr() {
if (DEBUG) {
log.log(LogService.LOG_DEBUG, "MotherBUGTweetNTwitch: Trying to post...");
}
HttpClient client = new HttpClient();
client.getParams().setParameter("http.useragent", "BUG");
client.getParams().setAuthenticationPreemptive(true);
HostConfiguration host = client.getHostConfiguration();
try {
host.setHost(new URI("http://twitxr.com", true));
} catch (URIException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (NullPointerException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Credentials credentials;
try {
credentials = new UsernamePasswordCredentials(this.TWITXR_LOGIN,
toMD5(this.TWITXR_PASSWD));
AuthScope authScope = new AuthScope(AuthScope.ANY_HOST,
AuthScope.ANY_PORT);
HttpState state = client.getState();
state.setCredentials(authScope, credentials);
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
PostMethod post = new PostMethod("/api/rest/postUpdate");
post.getParams().setBooleanParameter(
HttpMethodParams.USE_EXPECT_CONTINUE, true);
try {
if (DEBUG) {
log.log(LogService.LOG_DEBUG, " MotherBUGTweetNTwitch: Uploading " + this.PATH + " to "
+ host.getHost());
}
Part[] parts = {
new FilePart("image", new File(this.PATH)),
new StringPart("text", "Motion detected! http://twitter.com/"+TWITTER_LOGIN),
new StringPart("source", "le BUG"),
new StringPart("place", "915 Broadway, New York, NY, 10010") };
post.setRequestEntity(new MultipartRequestEntity(parts, post
.getParams()));
client.getHttpConnectionManager().getParams().setConnectionTimeout(
5000);
int status = client.executeMethod(host, post);
System.err.println(post.getStatusLine());
System.err.println(post.getResponseBodyAsString());
if (status == HttpStatus.SC_OK) {
log.log(LogService.LOG_DEBUG, "MotherBUGTweetNTwitch: Upload complete, response:\n"
+ post.getResponseBodyAsString());
} else {
log.log(LogService.LOG_DEBUG, "MotherBUGTweetNTwitch: Upload failed, response:\n"
+ HttpStatus.getStatusText(status));
System.err.println("Upload failed, response:\n"
+ HttpStatus.getStatusText(status));
}
} catch (Exception e) {
log.log(LogService.LOG_ERROR, "MotherBUGTweetNTwitch: Error: " + e.getMessage());
System.err.println("MotherBUGTweetNTwitch: Error: " + e.getMessage());
e.printStackTrace();
} finally {
post.releaseConnection();
}
}
// helpers
public static String toMD5(String s) throws NoSuchAlgorithmException {
MessageDigest m = MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
return (new BigInteger(1, m.digest()).toString(16));
}
public void writeImageToFile() {
if (DEBUG) {
System.out.println("Opening " + this.PATH + " to write");
log.log(log.LOG_DEBUG, "Opening " + this.PATH + " to write");
}
File temp = new File(this.PATH);
FileOutputStream fos;
try {
fos = new FileOutputStream(temp);
if (DEBUG) {
System.out.println("Writing " + this.PATH);
log.log(log.LOG_DEBUG, "Writing " + this.PATH);
}
fos.write(cam.getImage());
fos.close();
if (DEBUG) {
System.out.println("Wrote " + this.PATH);
log.log(log.LOG_DEBUG, "Wrote " + this.PATH);
}
} catch (Exception e) {
// TODO Auto-generated catch block
log.log(log.LOG_ERROR, e.getMessage());
e.printStackTrace();
}
}
}