Skip to content

Instantly share code, notes, and snippets.

@ymnk
Created December 10, 2009 08:37
Show Gist options
  • Save ymnk/253208 to your computer and use it in GitHub Desktop.
Save ymnk/253208 to your computer and use it in GitHub Desktop.
The included pom.xml expects wave-robot-api-20090916.jar is stored in the repository.
* Donwload that jar file from its donwload site[1],
* Install that file into your repository with following command,
mvn install:install-file \
-Dfile=wave-robot-api-20090916.jar \
-DgroupId=com.google.wave \
-DartifactId=wave-robot-api \
-Dversion=20090916 \
-Dpackaging=jar \
-DgeneratePom=true
[1] http://code.google.com/p/wave-robot-java-client/downloads/list
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>????</application>
<version>1</version>
<system-properties>
<property name="in.gae.j" value="true" />
</system-properties>
<sessions-enabled>true</sessions-enabled>
<static-files>
<exclude path="/**" />
</static-files>
</appengine-web-app>
package com.jcraft.wave_bot
import _root_.java.io.File
import _root_.junit.framework._
import Assert._
import _root_.scala.xml.XML
import _root_.net.liftweb.util._
import _root_.net.liftweb.common._
object AppTest {
def suite: Test = {
val suite = new TestSuite(classOf[AppTest])
suite
}
def main(args : Array[String]) {
_root_.junit.textui.TestRunner.run(suite)
}
}
/**
* Unit test for simple App.
*/
class AppTest extends TestCase("app") {
/**
* Rigourous Tests :-)
*/
def testOK() = assertTrue(true)
// def testKO() = assertTrue(false);
/**
* Tests to make sure the project's XML files are well-formed.
*
* Finds every *.html and *.xml file in src/main/webapp (and its
* subdirectories) and tests to make sure they are well-formed.
*/
def testXml() = {
var failed: List[File] = Nil
def handledXml(file: String) =
file.endsWith(".xml")
def handledXHtml(file: String) =
file.endsWith(".html") || file.endsWith(".htm") || file.endsWith(".xhtml")
def wellFormed(file: File) {
if (file.isDirectory)
for (f <- file.listFiles) wellFormed(f)
if (file.isFile && handledXml(file.getName)) {
try {
XML.loadFile(file)
} catch {
case e: _root_.org.xml.sax.SAXParseException => failed = file :: failed
}
}
if (file.isFile && handledXHtml(file.getName)) {
PCDataXmlParser(new _root_.java.io.FileInputStream(file.getAbsolutePath)) match {
case Full(_) => // file is ok
case _ => failed = file :: failed
}
}
}
wellFormed(new File("src/main/webapp"))
val numFails = failed.size
if (numFails > 0) {
val fileStr = if (numFails == 1) "file" else "files"
val msg = "Malformed XML in " + numFails + " " + fileStr + ": " + failed.mkString(", ")
println(msg)
fail(msg)
}
}
}
package bootstrap.liftweb
import _root_.net.liftweb.common._
import _root_.net.liftweb.util._
import _root_.net.liftweb.http._
import _root_.net.liftweb.sitemap._
import _root_.net.liftweb.sitemap.Loc._
import Helpers._
import _root_.com.jcraft.wave_bot.lib.Gadget
/**
* A class that's instantiated early and run. It allows the application
* to modify lift's environment
*/
class Boot {
def boot {
// where to search snippet
LiftRules.addToPackages("com.jcraft.wave_bot")
// Build SiteMap
val entries = Menu(Loc("Home", List("index"), "Home")) :: Nil
LiftRules.setSiteMap(SiteMap(entries:_*))
Gadget.init()
ResourceServer.allow {
case "gadget" :: _ => true
}
LiftRules.liftRequest.append {
case Req("_wave" :: _, _, _) => false
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<w:robot xmlns:w="http://wave.google.com/extensions/robots/1.0">
<w:capabilities>
<w:capability name="WAVELET_PARTICIPANTS_CHANGED" content="true" />
<w:capability name="BLIP_SUBMITTED" content="true" />
<w:capability name="DOCUMENT_CHANGED" content="true" />
<!--<w:capability name="WAVELET_BLIP_CREATED" content="true" />-->
</w:capabilities>
<w:profile name="HelloRobot" imageurl="http://upload.wikimedia.org/wikipedia/commons/thumb/0/05/Robot_icon.svg/200px-Robot_icon.svg.png" />
<w:version>1</w:version>
</w:robot>
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="State Example" height="120">
<Require feature="wave" />
<Require feature="dynamic-height" />
</ModulePrefs>
<Content type="html">
<![CDATA[
<div id="content_div" style="height: 50px;"></div>
<script type="text/javascript">
var div = document.getElementById('content_div');
function buttonClicked() {
var value = parseInt(wave.getState().get('count', '0'));
wave.getState().submitDelta({'count': value + 1});
}
function stateUpdated() {
if(!wave.getState().get('count')) {
div.innerHTML = "The count is 0.";
}
else {
var message = "The count is " + wave.getState().get('count');
if(wave.getState().get('count-bot')) {
message += wave.getState().get('count-bot');
}
div.innerHTML = message;
}
gadgets.window.adjustHeight();
}
function init() {
if (wave && wave.isInWaveContainer()) {
wave.setStateCallback(stateUpdated);
gadgets.window.adjustHeight();
}
}
gadgets.util.registerOnLoadHandler(init);
// Reset value of "count" to 0
function resetCounter(){
wave.getState().submitDelta({'count': '0'});
}
</script>
<input type=button value="Click Me!" id="butCount" onClick="buttonClicked()">
<input type=button value="Reset" id="butReset" onClick="resetCounter()">
]]>
</Content>
</Module>
<?xml version="1.0" encoding="UTF-8"?>
<cronentries/>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<title>Google Wave Robot Sample</title>
<script id="jquery" src="/classpath/jquery.js" type="text/javascript"></script>
</head>
<body>
<lift:bind name="content" />
<!-- <lift:Menu.builder /> -->
<lift:msgs/>
</body>
</html>
package com.jcraft.wave_bot.lib
import _root_.net.liftweb._
import http._
import util._
import common._
import Helpers._
import _root_.scala.xml.XML
import _root_.com.google.wave.api._
object Gadget{
var static_gadgets:List[java.io.File] = Nil
def init()={
LiftRules.dispatch.prepend(NamedPF("Gadget dispatch") {
case Req("gadget":: gadget :: Nil, _, GetRequest) => dispatch(gadget)
})
static_gadgets = (new java.io.File("gadget")).listFiles.toList
}
private def dispatch(gadget:String)()={
static_gadgets.find(_.getName == gadget+".xml") match{
case Some(g) => Full(XmlResponse(XML.loadFile(g)))
case _ =>
val param = S.param("param") openOr "default"
Full(XmlResponse(
<Module>
<ModulePrefs title="Hello Wave">
<Require feature="wave" />
</ModulePrefs>
<Content type="html">
{PCData("Hello, Wave!!")}
</Content>
</Module>))
}
}
def handle(gv:GadgetView, g:Gadget)={
if(g.getUrl.endsWith("count.xml")){
val count = g.getField("count")
g.setField("count-bot", count)
// We need to replace that gadget to pass data to it ;-<
// http://code.google.com/p/google-wave-resources/issues/detail?id=406
gv.replace(g.getUrl, g)
}
else if(g.getUrl.endsWith("rate-it.xml")){
var image = g.getField("image")
if(image==null || image.length <= 163){
image = toBijin(-1, -1)
}
var time = image.substring(159, 161).toInt * 60 + image.substring(161, 163).toInt
/*
if(image==null || image.length <= 78){
image = toBijin(-1, -1)
}
var time = image.substring(74, 76).toInt * 60 + image.substring(76, 78).toInt
*/
g.getField("operation") match{
case "next" =>
time += 1; if(time==24*60+0){time = 0}
image = toBijin(time/60, time%60)
g.setField("image", image)
g.setField("count", Cache.apply(image))
g.setField("operation", "")
gv.replace(g.getUrl, g)
case "prev" =>
time -= 1; if(time==0){time = 23*60+59}
image = toBijin(time/60, time%60)
g.setField("image", image)
g.setField("count", Cache.apply(image))
g.setField("operation", "")
gv.replace(g.getUrl, g)
case _ =>
Cache += (image, g.getField("count"))
}
println(g.getField("count"))
println(g.getField("image"))
}
}
def toBijin(h:Int, m:Int)={
import java.util._
import java.text._
val date = new Date
val time_format = new SimpleDateFormat("HHmm")
val day_format = new SimpleDateFormat("yyyyMMdd")
val timezone = TimeZone.getTimeZone("GMT+9")
time_format.setTimeZone(timezone)
day_format.setTimeZone(timezone)
val time = if(h == -1 && m == -1){
time_format.format(date)
}
else{
(if(h<10){"0"+h.toString}else{h.toString}) + (if(m<10){"0"+m.toString}else{m.toString})
}
// "http://www.google.com/chart?chst=d_bubble_texts_big&chld=bb|01D5C1|FFFFFF|"+time
"http://www.ig.gmodules.com/gadgets/proxy/refresh=3600&container=ig&"+
"gadget=http%3A%2F%2Fbijint.com%2Fbijint.xml/"+
"http://www.bijint.com/assets/pict/bijin/240x320/"+
time+".jpg?yyyymmdd="+
day_format.format(date) +"&ext=.jpg"
}
}
object Cache{
import java.util.Collections
import javax.cache.Cache
import javax.cache.CacheException
import javax.cache.CacheManager
import _root_.scala.collection.mutable.HashMap
val cache2 = HashMap.empty[String,String]
val cacheFactory = CacheManager.getInstance.getCacheFactory
val cache:Cache[_, AnyRef] = cacheFactory.createCache(new java.util.HashMap[String, AnyRef])
def +=(k:String, v:String):Unit = synchronized{
cache.asInstanceOf[java.util.Map[String, AnyRef]].put(k, v)
cache2.put(k, v)
}
def apply(k:String):String = synchronized{
cache2.get(k) getOrElse "0"
/*
val value = cache.get(k)
if(value!=null) "0"
else value.asInstanceOf[String]
*/
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Hello Wave" height="500">
<Require feature="wave" />
<Require feature="dynamic-height" />
</ModulePrefs>
<Content type="html">
<![CDATA[
<script type="text/javascript">
function init() {
if (wave && wave.isInWaveContainer()) {
gadgets.window.adjustHeight();
}
}
gadgets.util.registerOnLoadHandler(init);
</script>
Hello, Wave!
]]>
</Content>
</Module>
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="Hello Wave">
<Require feature="wave" />
</ModulePrefs>
<Content type="html">
<![CDATA[
Hello, Wave!
]]>
</Content>
</Module>
package com.jcraft.wave_bot.servlet
import com.google.wave.api._
import scala.collection.jcl.Conversions.convertList
import _root_.com.jcraft.wave_bot.snippet.{Wavelet => MyWavelet}
import _root_.com.jcraft.wave_bot.lib.Gadget
class HelloRobotScalaService extends AbstractRobotServlet {
implicit def c2i[A](c:java.util.Collection[A]):Iterator[A] = new Iterator[A]{
val iterator = c.iterator
def next():A = iterator.next
def hasNext:Boolean = iterator.hasNext
}
override def processEvents(bundle:RobotMessageBundle) {
val wavelet = bundle.getWavelet
if (bundle.wasSelfAdded) {
val blip = wavelet.appendBlip
val textView = blip.getDocument
val url = "http://www.google.com/chart?chst=d_bubble_texts_big&chld=bb|01D5C1|FFFFFF|Hello,+I+'m+a+test+robot!"
val image = new Image
image.setUrl(url)
textView.appendElement(image)
// textView.append("Hello, I'm a test robot!")
}
import EventType._
bundle.getEvents foreach { e => e.getType match{
case WAVELET_PARTICIPANTS_CHANGED =>
e.getAddedParticipants .
filter (!_.endsWith("appspot.com")) .
foreach { participantName =>
val blip = wavelet.appendBlip
val textView = blip.getDocument
textView.append("Welcome, " + participantName + "!")
}
case DOCUMENT_CHANGED =>
val gv = e.getBlip.getDocument.getGadgetView
for(g <- gv.getGadgets){
Gadget.handle(gv, g)
}
case BLIP_SUBMITTED =>
val gv = e.getBlip.getDocument.getGadgetView
for(g <- gv.getGadgets){
Gadget.handle(gv, g)
}
val submittedText = e.getBlip.getDocument.getText
Gadget.static_gadgets.find(_.getName.endsWith(submittedText)).foreach{f =>
e.getBlip.getDocument.delete
val gadget = new Gadget("http://lift-test.appspot.com/gadget/"+f.getName)
e.getBlip.getDocument.appendElement(gadget)
}
case et =>
}}
}
}
<lift:surround with="default" at="content">
<h2>Google Wave Robot Sample</h2>
<p>
<lift:Wavelet>
<wavelets:list />
</lift:Wavelet>
</p>
</lift:surround>
import _root_.bootstrap.liftweb.Boot
import _root_.scala.tools.nsc.MainGenericRunner
object LiftConsole {
def main(args : Array[String]) {
// Instantiate your project's Boot file
val b = new Boot()
// Boot your project
b.boot
// Now run the MainGenericRunner to get your repl
MainGenericRunner.main(args)
// After the repl exits, then exit the scala script
exit(0)
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jcraft.wave_bot</groupId>
<artifactId>googlewave-robot-sample</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>googlewave-robot-sample</name>
<inceptionYear>2007</inceptionYear>
<properties>
<scala.version>2.7.7</scala.version>
</properties>
<repositories>
<repository>
<id>scala-tools.org</id>
<name>Scala-Tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</repository>
<repository>
<id>scala-tools.org.snapshots</id>
<name>Scala-Tools Maven2 Repository for Snapshots</name>
<url>http://scala-tools.org/repo-snapshots</url>
<snapshots/>
</repository>
<repository>
<id>mvnsearch</id>
<url>http://www.mvnsearch.org/maven2</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>scala-tools.org</id>
<name>Scala-Tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-util</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-webkit</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-mapper</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>[6.1.6, 6.1.19)</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>com.google.wave</groupId>
<artifactId>wave-robot-api</artifactId>
<version>20090916</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20090211</version>
</dependency>
<dependency>
<groupId>com.metaparadigm</groupId>
<artifactId>json-rpc</artifactId>
<version>1.0</version>
</dependency>
<!-- for LiftConsole -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-compiler</artifactId>
<version>${scala.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<contextPath>/</contextPath>
<scanIntervalSeconds>5</scanIntervalSeconds>
</configuration>
</plugin>
<plugin>
<groupId>net.sf.alchim</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compress</goal>
</goals>
</execution>
</executions>
<configuration>
<nosuffix>true</nosuffix>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-idea-plugin</artifactId>
<configuration>
<downloadSources>true</downloadSources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<downloadSources>true</downloadSources>
<excludes>
<exclude>org.scala-lang:scala-library</exclude>
</excludes>
<classpathContainers>
<classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
</classpathContainers>
<projectnatures>
<java.lang.String>ch.epfl.lamp.sdt.core.scalanature</java.lang.String>
<java.lang.String>org.eclipse.jdt.core.javanature</java.lang.String>
</projectnatures>
<buildcommands>
<java.lang.String>ch.epfl.lamp.sdt.core.scalabuilder</java.lang.String>
</buildcommands>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
</configuration>
</plugin>
</plugins>
</reporting>
</project>
package com.jcraft.wave_bot.servlet
import com.google.wave.api._
import scala.collection.jcl.Conversions.convertList
import _root_.com.jcraft.wave_bot.snippet.{Wavelet => MyWavelet}
class Profile extends ProfileServlet {
override def getRobotName() = "robot"
override def getRobotAvatarUrl() = "http://lift-test.appspot.com/_wave/robot/profile.png"
override def getRobotProfilePageUrl() = "http://lift-test.appspot.com/"
}
<?xml version="1.0" encoding="UTF-8" ?>
<Module>
<ModulePrefs title="State Example" height="320">
<Require feature="wave" />
<Require feature="dynamic-height" />
</ModulePrefs>
<Content type="html">
<![CDATA[
<div id="content_div"></div>
<script type="text/javascript">
var div = document.getElementById('content_div');
function buttonNext() {
wave.getState().submitDelta({'operation': 'next'});
}
function buttonPrev() {
wave.getState().submitDelta({'operation': 'prev'});
}
function buttonClicked() {
var value = parseInt(wave.getState().get('count', '0'));
wave.getState().submitDelta({'count': value + 1, 'operation' : ''});
}
function stateUpdated() {
if(!wave.getState().get('count')) {
div.innerHTML = "The clicked count is 0."
}
else {
div.innerHTML = "The clicked count is " + wave.getState().get('count');
}
if(wave.getState().get('image')) {
var _img=new Image();
_img.onload=function(){
var img = document.getElementById('image');
img.src = wave.getState().get('image');
gadgets.window.adjustHeight();
}
_img.src=wave.getState().get('image');
}
}
function init() {
if (wave && wave.isInWaveContainer()) {
wave.setStateCallback(stateUpdated);
gadgets.window.adjustHeight();
}
}
gadgets.util.registerOnLoadHandler(init);
</script>
<input type=button value="<<" id="prev" onClick="buttonPrev()">
<input type=button value=">>" id="next" onClick="buttonNext()">
<input type=button value="Click Me!" id="butCount" onClick="buttonClicked()">
<br />
<img id="image" src="http://lift-test.appspot.com/_wave/robot/profile.png"/>
]]>
</Content>
</Module>
import _root_.org.mortbay.jetty.Connector
import _root_.org.mortbay.jetty.Server
import _root_.org.mortbay.jetty.webapp.WebAppContext
import org.mortbay.jetty.nio._
object RunWebApp extends Application {
val server = new Server
val scc = new SelectChannelConnector
scc.setPort(8080)
server.setConnectors(Array(scc))
val context = new WebAppContext()
context.setServer(server)
context.setContextPath("/")
context.setWar("src/main/webapp")
server.addHandler(context)
try {
println(">>> STARTING EMBEDDED JETTY SERVER, PRESS ANY KEY TO STOP")
server.start()
while (System.in.available() == 0) {
Thread.sleep(5000)
}
server.stop()
server.join()
} catch {
case exc : Exception => {
exc.printStackTrace()
System.exit(100)
}
}
}
package com.jcraft.wave_bot.snippet
import _root_.scala.xml.NodeSeq
import _root_.net.liftweb.util.Helpers
import Helpers._
import _root_.scala.collection.mutable.HashMap
import _root_.scala.collection.jcl.Conversions._
import _root_.com.google.wave.api.{Wavelet => GWavelet}
import _root_.com.jcraft.wave_bot.lib._
class Wavelet {
def render(in: NodeSeq): NodeSeq =
Helpers.bind("wavelets", in,
"list" ->
<ul>
{for((k, v) <- Cache.cache2 if(v!="0")) yield list(k, v)}
</ul>)
private def list(k:String, v:String) = {
<li>
<img src={k}>{k}</img> {v}
</li>
}
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<filter>
<filter-name>LiftFilter</filter-name>
<display-name>Lift Filter</display-name>
<description>The Filter that intercepts lift calls</description>
<filter-class>net.liftweb.http.LiftFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LiftFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>HelloRobot</servlet-name>
<servlet-class>com.jcraft.wave_bot.servlet.HelloRobotScalaService</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloRobot</servlet-name>
<url-pattern>/_wave/robot/jsonrpc</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Profile</servlet-name>
<servlet-class>com.jcraft.wave_bot.servlet.Profile</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Profile</servlet-name>
<url-pattern>/_wave/robot/profile</url-pattern>
</servlet-mapping>
</web-app>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment