Skip to content

Instantly share code, notes, and snippets.

@kaja47
Created June 28, 2011 09:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kaja47/1050801 to your computer and use it in GitHub Desktop.
Save kaja47/1050801 to your computer and use it in GitHub Desktop.
stripbot source
scalaVersion := "2.9.0-1"
libraryDependencies += "commons-codec" % "commons-codec" % "1.4"
libraryDependencies += "oauth.signpost" % "signpost-core" % "1.2.1.1"
libraryDependencies += "org.jdom" % "jdom" % "1.1"
libraryDependencies += "rome" % "rome" % "0.9"
libraryDependencies += "jtwitter" % "jtwitter" % "1.8.3" from "http://www.winterwell.com/software/jtwitter/jtwitter.jar"
// https://twitter.com/#!/stripbot
package stripBot
import scala.actors.Actor
import scala.actors.Actor._
import java.net.{ URL, URLEncoder, MalformedURLException }
import java.io.{ FileReader, BufferedReader, FileWriter, IOException }
import java.util.Date
import com.sun.syndication.feed.synd.SyndEntry
import com.sun.syndication.io.{ SyndFeedInput, XmlReader }
import winterwell.jtwitter.{ Twitter, OAuthSignpostClient }
object Main extends App {
val tweeter = new Tweeter("stripbot", Config.oauthKey, Config.oauthSecret)
val rssFetcher = new Fetcher(Config.sources, tweeter)
rssFetcher.start
tweeter.start
}
object Config {
val sources = Seq(
StripSource("Bugemos", "http://bugemos.com/?q=rss.xml"),
StripSource("balónek strip", "http://lubosbranda.blog.idnes.cz/rss/"),
StripSource("bezejmenný hrdina", "http://picasaweb.google.com/data/feed/base/user/marusjakub/albumid/5110845406354432785?alt=rss&kind=photo&hl=cs"),
StripSource("ITBiz", "http://www.itbiz.cz/taxonomy/term/25/0/feed"),
StripSource("iDNES komix", "http://servis.idnes.cz/rss.asp?c=komiksy"),
StripSource("XKCD", "http://xkcd.com/rss.xml", "EN"),
StripSource("Questionable Content", "http://www.questionablecontent.net/QCRSS.xml", "EN"),
StripSource("Johny Wander", "http://www.johnnywander.com/feed", "EN"),
StripSource("Wasted Talent", "http://feeds2.feedburner.com/WastedTalentRss", "EN"),
StripSource("chainsawsuit", "http://feeds.feedburner.com/Chainsawsuit", "EN"),
StripSource("Cyanide & happyiness", "http://feeds.feedburner.com/Explosm", "EN", { e => e.getTitle.matches("""\d+\.\d+.\d+""") })
)
val oauthKey = "---YOUR-OAUTH-KEY---"
val oauthSecret = "---YOUR-OAUTH-SECRET---"
}
// --- main actors
class Fetcher(sources: Seq[StripSource], tweeter: Tweeter) extends Actor {
val lastCheckFile = "lastCheck.txt"
def checkRss(source: StripSource, lastCheck: Date) =
for {
e <- Util.readRssFromUrl(source.rssUrl)
if source.filterFn(e)
if e.getPublishedDate != null && e.getPublishedDate.after(lastCheck)
} yield e
def formatMessage(source: StripSource, entry: SyndEntry) =
source.name+" - "+entry.getTitle.trim+" "+Util.shortenUrl(entry.getLink)+" "+source.lang+" #stripy"
def getLastCheck: Date = {
var line = Util readLine lastCheckFile
if (line == null || line == "") new Date() else new Date(line.toLong)
}
def setLastCheck = Util.writeLine(lastCheckFile, "" + new Date().getTime)
def act {
println(sources.mkString("\n"))
loop {
var lastCheck = getLastCheck
println("Last check: " + lastCheck)
for {
source <- sources
entry <- checkRss(source, lastCheck)
} tweeter ! formatMessage(source, entry)
setLastCheck
Thread.sleep(1000 * 60 * 60) // 1 hour
}
}
}
class Tweeter(username: String, oauthKey: String, oauthSecret: String) extends Actor {
val accessTokensFile = "accessTokens.txt"
def connectToTwitter = {
val tokens = Util readLine accessTokensFile
val oauthClient =
if (tokens.nonEmpty) {
val t = tokens.split(" ")
new OAuthSignpostClient(oauthKey, oauthSecret, t(0), t(1))
} else {
val oauthClient = new OAuthSignpostClient(oauthKey, oauthSecret, "oob")
oauthClient.authorizeDesktop()
val v = OAuthSignpostClient.askUser("Please enter the verification PIN from Twitter")
oauthClient.setAuthorizationCode(v)
Util.writeLine(accessTokensFile, oauthClient.getAccessToken.mkString(" "))
oauthClient
}
new Twitter(username, oauthClient)
}
def act {
val twitter = connectToTwitter
loop {
react {
case m =>
twitter.updateStatus(m.toString)
println("tweeted: " + m.toString)
}
}
}
}
// ----
case class StripSource (name: String, rssUrl: String, lang: String = "CZ", filterFn: SyndEntry => Boolean = x => true)
object Util {
def readLine(f: String) = io.Source.fromFile(f).getLines.next
def writeLine(f: String, data: String) = {
val fw = new FileWriter(f)
fw write data
fw.close()
}
def readRssFromUrl(url: String): List[SyndEntry] = {
try {
val i = new SyndFeedInput().build(new XmlReader(new URL(url))).getEntries
List(i.toArray(new Array[SyndEntry](0)) : _*)
} catch {
case _ => List()
}
}
def shortenUrl(url: String) = {
val line = io.Source.fromURL("http://url.k47.cz/api/get/?url=" + URLEncoder.encode(url, "UTF8")).getLines.next
if (line startsWith "ERROR") throw new MalformedURLException
line
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment