/src/main/scala/megane.scala
Scala | 1052 lines | 949 code | 72 blank | 31 comment | 121 complexity | c60b92b1d828282ffd4376d5206f418b MD5 | raw file
- package net.centrevillage.mgtw
- import java.awt.AlphaComposite
- import java.awt.Canvas
- import java.awt.Color
- import java.awt.Cursor
- import java.awt.FlowLayout
- import java.awt.Font
- import java.awt.FontMetrics
- import java.awt.Graphics
- import java.awt.Graphics2D
- import java.awt.GraphicsEnvironment
- import java.awt.Point
- import java.awt.color.ColorSpace
- import java.awt.event.KeyEvent
- import java.awt.event.MouseEvent
- import java.awt.geom.Ellipse2D
- import java.awt.geom._
- import java.awt.image._
- import java.awt.PopupMenu
- import java.awt.MenuItem
- import java.awt.SystemTray
- import java.awt.TrayIcon
- import java.awt.TrayIcon.MessageType
- import java.awt.event._
- import java.io.IOException
- import java.io.BufferedReader
- import java.io.File
- import java.io.FileInputStream
- import java.io.InputStream
- import java.io.OutputStream
- import java.io.InputStreamReader
- import java.io.FileReader
- import java.io.FileWriter
- import java.util.Timer
- import java.util.TimerTask
- import java.util.Date
- import java.net.URL
- import javax.imageio.ImageIO
- import twitter4j._
- import twitter4j.api._
- import twitter4j.auth._
- import twitter4j.conf._
- import twitter4j.json._
- import twitter4j.management._
- import twitter4j.media._
- import twitter4j.util._
- import org.yaml.snakeyaml.Yaml
- import scala.collection.JavaConversions._
- import scala.collection.immutable.SortedMap
- import scala.collection.immutable.TreeMap
- import scala.collection.mutable.ArrayBuffer
- import scala.collection.mutable.HashSet
- import scala.collection.mutable.Queue
- import scala.collection.mutable.SynchronizedQueue
- import scala.collection.mutable.{LinkedHashMap => LHMap}
- import scala.collection.mutable.{ListBuffer => MList}
- import scala.collection.mutable.{Map => MMap}
- import scala.io._
- import scala.util.matching.Regex
- import scala.util.parsing.combinator._
- import scala.swing._
- import scala.swing.event.{Event => ScEvent, ActionEvent => ScActionEvent, _}
- import scala.swing.Dialog
- import javax.swing.JTable
- import javax.swing.table.DefaultTableModel
- import javax.swing.SwingUtilities
- import javax.swing.UIManager
- import javax.swing.ImageIcon
- import javax.swing.event.HyperlinkEvent
- import javax.swing.event.HyperlinkEvent.EventType
- import javax.swing.event.HyperlinkListener
- import java.awt.Desktop
- case class ResourceNotFoundException(msg: String = "Unknown", cause: Throwable = null) extends Exception(msg, cause)
- object Consts {
- val MAX_ROW = 10000
- }
- class ConfigLoader {
- private def getResourceStream(path: String): java.io.InputStream = {
- val file = new File(path)
- val resourcePath = path.replaceAll("""^\./""", "/")
- var url = getClass.getClassLoader.getResource(resourcePath)
- if (file.exists) {
- new FileInputStream(path)
- } else if (url != null) {
- url.openStream()
- } else {
- throw ResourceNotFoundException("????[" + path + "]???????")
- }
- }
- protected var cache = MMap[String, Map[Any, Any]]()
- private def loadReal(path: String): Map[Any, Any] = loadReal(path, Map[Symbol, Any]())
- private def loadReal(path: String, opt: Map[Symbol, Any]): Map[Any, Any] = {
- var is: InputStream = null
- var reader: InputStreamReader = null
- try {
- is = getResourceStream(path)
- reader = new InputStreamReader(is)
- convertConfigMap((new Yaml).load(reader).asInstanceOf[java.util.HashMap[Any, Any]])
- } finally {
- reader.close()
- is.close()
- }
- }
- def clearCache() {
- cache.clear()
- }
- def load(path: String): Map[Any, Any] = {
- load(path, Map[Symbol, Any]())
- }
- def load(path: String, opt: Map[Symbol, Any]): Map[Any, Any] = {
- try {
- opt.get('cache) match {
- case Some(true) =>
- cache.get(path) match {
- case Some(config) => config
- case None =>
- val config = loadReal(path)
- cache += (path -> config)
- config
- }
- case _ => loadReal(path)
- }
- } catch {
- case ex: Exception =>
- Dialog.showMessage(title="ERROR", message="["+ path +"]???????????")
- throw ex
- }
- }
- def save(path: String, values: Map[Any, Any]) {
- var writer = new FileWriter(path)
- try {
- (new Yaml).dump(unconvertConfigMap(values), writer)
- } finally {
- writer.close()
- }
- }
- private def unconvertConfigMap(v: Map[Any, Any]): java.util.Map[Any, Any] = {
- import java.util.HashMap
- val map = new HashMap[Any, Any]
- v.foreach {
- case (k, value: Map[Any, Any]) => map += (k -> unconvertConfigMap(value))
- case (k, value: List[Any]) => map += (k -> unconvertConfigList(value))
- case (k, value) => map += (k -> value)
- }
- map
- }
- private def unconvertConfigList(v: List[Any]): java.util.List[Any] = {
- import java.util.ArrayList
- val list = new ArrayList[Any]
- v.foreach {
- case value: Map[Any, Any] => list += unconvertConfigMap(value)
- case value: List[Any] => list += unconvertConfigList(value)
- case value => list += value
- }
- list
- }
- private def convertConfigMap(v: java.util.Map[Any, Any]): Map[Any, Any] = {
- val map = v
- var immutableMap = Map[Any, Any]()
- map.foreach {
- case (k, vv: java.util.Map[Any, Any]) => immutableMap += (k -> convertConfigMap(vv))
- case (k, vv: java.util.List[Any]) => immutableMap += (k -> convertConfigList(vv))
- case (k, vv) => immutableMap += (k -> vv)
- }
- immutableMap
- }
- private def convertConfigList(v: java.util.List[Any]): List[Any] = {
- v.toList.map {
- case vv: java.util.Map[Any, Any] => convertConfigMap(vv)
- case vv: java.util.List[Any] => convertConfigList(vv)
- case vv => vv
- }
- }
- }
- class MGLayout extends java.awt.LayoutManager {
- import java.awt._
- def addLayoutComponent(name: String, comp: Component): Unit = {}
- def removeLayoutComponent(comp: Component): Unit = {}
- def preferredLayoutSize(target: Container): Dimension =
- target.getComponents.map(_.getPreferredSize).foldLeft(new Dimension) { (d, sz) =>
- d.width = math.max(d.width, sz.width)
- d.height += sz.height
- d
- }
- def minimumLayoutSize(target: Container): Dimension =
- target.getComponents.map(_.getMinimumSize).foldLeft(new Dimension) { (d, sz) =>
- d.width = math.max(d.width, sz.width)
- d.height += sz.height
- d
- }
- def layoutContainer(target: Container): Unit = {
- var y = 0
- target.getComponents.foreach { c =>
- val sz = c.getPreferredSize
- c.setBounds(0, y, sz.width, sz.height)
- y += sz.height
- }
- }
- }
- class ListPane extends Panel with SequentialContainer.Wrapper {
- override lazy val peer = new javax.swing.JPanel(new MGLayout) with SuperMixin
- }
- object ConfigLoader extends ConfigLoader
- class HyperlinkHandler extends HyperlinkListener {
- def hyperlinkUpdate(e: HyperlinkEvent) {
- if (e.getEventType() == EventType.ACTIVATED) {
- val url = e.getURL()
- val dp = Desktop.getDesktop()
- try {
- dp.browse(url.toURI())
- } catch {
- case ex: Exception => ex.printStackTrace()
- }
- }
- }
- }
- class MGTimeLine extends scala.swing.Table {
- val columnNames = Array[java.lang.Object](
- "????", "ID", "????", "M", "??"
- )
- override protected def editor(row: Int, column: Int) = {
- null
- }
- override lazy val model = new MyTableModel(super.model.asInstanceOf[javax.swing.table.DefaultTableModel])
- columnNames.foreach((model.asInstanceOf[MyTableModel]).addColumn(_))
- peer.getColumnModel().getColumn(0).setPreferredWidth(60)
- peer.getColumnModel().getColumn(0).setMaxWidth(60)
- peer.getColumnModel().getColumn(0).setMinWidth(20)
- peer.getColumnModel().getColumn(1).setPreferredWidth(64)
- peer.getColumnModel().getColumn(1).setMaxWidth(80)
- peer.getColumnModel().getColumn(1).setMinWidth(50)
- peer.getColumnModel().getColumn(2).setPreferredWidth(400)
- peer.getColumnModel().getColumn(2).setMaxWidth(2048)
- peer.getColumnModel().getColumn(2).setMinWidth(128)
- peer.getColumnModel().getColumn(3).setPreferredWidth(20)
- peer.getColumnModel().getColumn(3).setMaxWidth(20)
- peer.getColumnModel().getColumn(3).setMinWidth(20)
- peer.getColumnModel().getColumn(4).setPreferredWidth(50)
- peer.getColumnModel().getColumn(4).setMaxWidth(128)
- peer.getColumnModel().getColumn(4).setMinWidth(50)
- }
- class MGTWindow {
- val frameWidth = 800
- val frameHeight = 600
- val rowHeight = 30
- lazy val timeLine = new MGTimeLine
- lazy val replyLine = new MGTimeLine
- lazy val dmLine = new MGTimeLine
- lazy val myText = new scala.swing.TextArea {
- yLayoutAlignment = 1.0
- charWrap = true
- lineWrap = true
- background = new Color(208, 208, 208)
- border = javax.swing.plaf.basic.BasicBorders.getTextFieldBorder
- maximumSize = new Dimension(9999, 60)
- yLayoutAlignment = java.awt.Component.BOTTOM_ALIGNMENT
- }
- lazy val selImg = new Label
- lazy val selMsg = new EditorPane("text/html", "") {
- import scala.swing.Alignment._
- editable = false
- minimumSize = new Dimension(100, 20)
- peer.putClientProperty(javax.swing.JEditorPane.HONOR_DISPLAY_PROPERTIES, java.lang.Boolean.TRUE)
- peer.setFont((new Label).font)
- peer.addHyperlinkListener(new HyperlinkHandler)
- override def text_= (txt: String) {
- super.text_=(txt.replaceAll("https?(:\\/\\/[-_.!~*\\'a-zA-Z0-9\\/?\\@&=+\\$%#]+)", "<a href='$0'>$0</a>"))
- }
- }
- lazy val top = new Frame {
- import FlowPanel.Alignment._
-
- override def closeOperation() {
- iconified()
- }
- // iconImage = ImageIO.read(classOf[Megane].getResourceAsStream("icon.png"))
- iconImage = ImageIO.read(classOf[Megane].getResourceAsStream("icon64.png"))
- size = new Dimension(frameWidth, frameHeight)
- title = "megane-twit"
- contents = new BoxPanel(Orientation.Vertical) {
- contents += new BoxPanel(Orientation.Vertical) {
- maximumSize = new Dimension(9999, 9999)
- contents += new TabbedPane {
- pages += new TabbedPane.Page("Home", new ScrollPane {
- yLayoutAlignment = 0.0
- contents = timeLine
- })
- pages += new TabbedPane.Page(" ? ", new ScrollPane {
- yLayoutAlignment = 0.0
- contents = replyLine
- })
- pages += new TabbedPane.Page(" DM ", new ScrollPane {
- yLayoutAlignment = 0.0
- contents = dmLine
- })
- }
- }
- contents += new BoxPanel(Orientation.Vertical) {
- maximumSize = new Dimension(9999, 100)
- contents += new BoxPanel(Orientation.Horizontal) {
- yLayoutAlignment = 1.0
- size = new Dimension(frameWidth, 30)
- maximumSize = new Dimension(9999, 80)
- contents += new FlowPanel(Left)(selImg)
- contents += selMsg
- }
- contents += myText
- contents += new BoxPanel(Orientation.Horizontal) {
- yLayoutAlignment = 1.0
- size = new Dimension(frameWidth, 26)
- contents += new FlowPanel(Left)(new BoxPanel(Orientation.Horizontal) {
- contents += new scala.swing.Button {
- name = "??????????"
- text = "DM"
- reactions += {
- case ButtonClicked(b) =>
- try {
- val text = myText.text.trim
- if (selectedTL != null && !text.isEmpty) {
- if (text.length > 140) {
- Dialog.showMessage(
- title="???????",
- message=(text.length-140) + "??????????"
- )
- } else {
- Dialog.showConfirmation(message="@" + selectedTL.screenName + "?DM???????") match {
- case Dialog.Result.Yes =>
- Megane.mgTwitter.sendDirectMessage(selectedTL.userID, text)
- myText.text = ""
- case _ => ()
- }
- }
- }
- } catch {
- case e: Exception => Dialog.showMessage(title="ERROR", message=e.getMessage)
- }
- ()
- }
- }
- contents += new scala.swing.Button {
- name = "??"
- text = "QT"
- reactions += {
- case ButtonClicked(b) =>
- try {
- if (selectedTL != null) {
- if (tabIdx == 3) {
- Dialog.showMessage(title="ERROR", message="??????????????????")
- } else {
- myText.text = "QT " + "@" + selectedTL.screenName + " " + selectedTL.msg
- }
- }
- } catch {
- case e: Exception => Dialog.showMessage(title="ERROR", message=e.getMessage)
- }
- ()
- }
- }
- contents += new scala.swing.Button {
- name = "?????"
- text = "RT"
- reactions += {
- case ButtonClicked(b) =>
- try {
- if (selectedTL != null) {
- if (tabIdx == 1) {
- Megane.mgTwitter.retweet(selectedTL.id)
- updateTLInfo(selectedTL.id, selectedTL.copy(relation="RT"))
- } else if (tabIdx == 2) {
- Megane.mgTwitter.retweet(selectedTL.id)
- updateReplyInfo(selectedTL.id, selectedTL.copy(relation="RT"))
- } else if (tabIdx == 3) {
- Dialog.showMessage(title="ERROR", message="?????????????????????")
- }
- }
- } catch {
- case e: Exception => Dialog.showMessage(title="ERROR", message=e.getMessage)
- }
- ()
- }
- }
- contents += new scala.swing.Button {
- name = "??"
- text = "?"
- reactions += {
- case ButtonClicked(b) =>
- try {
- if (selectedTL != null) {
- if (tabIdx == 3) {
- Dialog.showMessage(title="ERROR", message="??????????????????")
- } else {
- myText.text = "@" + selectedTL.screenName + " " + myText.text
- replyStatus = selectedTL.id
- replyScreenName = selectedTL.screenName
- }
- }
- } catch {
- case e: Exception => Dialog.showMessage(title="ERROR", message=e.getMessage)
- }
- ()
- }
- }
- contents += new scala.swing.Button {
- name = "?????"
- text = "?"
- reactions += {
- case ButtonClicked(b) =>
- try {
- if (selectedTL != null) {
- if (tabIdx == 1) {
- Megane.mgTwitter.fav(selectedTL.id)
- updateTLInfo(selectedTL.id, selectedTL.copy(relation="?"))
- } else if (tabIdx == 2) {
- Megane.mgTwitter.fav(selectedTL.id)
- updateReplyInfo(selectedTL.id, selectedTL.copy(relation="?"))
- } else if (tabIdx == 3) {
- Dialog.showMessage(title="ERROR", message="???????????????????????")
- }
- }
- } catch {
- case e: Exception => Dialog.showMessage(title="ERROR", message=e.getMessage)
- }
- ()
- }
- }
- })
- contents += new FlowPanel(Right)(new scala.swing.Button {
- name = "Submit"
- text = "??"
- reactions += {
- case ButtonClicked(b) =>
- try {
- val mgtw = Megane.mgTwitter
- val text = myText.text.trim
- if (!text.isEmpty) {
- if (text.length > 140) {
- Dialog.showMessage(
- title="???????",
- message=(text.length-140) + "??????????"
- )
- } else {
- if (replyScreenName != null && text.startsWith("@" + replyScreenName) && replyStatus > 0) {
- val reply = new StatusUpdate(text)
- reply.setInReplyToStatusId(replyStatus)
- mgtw.twit(reply)
- } else {
- mgtw.twit(text)
- }
- myText.text = ""
- }
- }
- Thread.sleep(1000)
- setTimeLine(mgtw.getHomeTimeLine())
- } catch {
- case e: Exception => Dialog.showMessage(title="ERROR", message=e.getMessage)
- }
- replyScreenName = null
- replyStatus = -1L
- ()
- }
- })
- }
- }
- listenTo(timeLine.selection)
- listenTo(dmLine.selection)
- listenTo(replyLine.selection)
- reactions += {
- case TableRowsSelected(source, range, adjusting) =>
- val selectedIdx = source.selection.rows.leadIndex
- if (source eq timeLine) {
- selectTwit(selectedIdx)
- } else if (source eq dmLine) {
- selectDM(selectedIdx)
- } else if (source eq replyLine) {
- selectReply(selectedIdx)
- }
- }
- }
- peer.setResizable(true)
- peer.setVisible(true)
- open()
- }
- var selectedTL: TimeLineInfo = null
- var replyScreenName: String = null
- var replyStatus: Long = -1L
- var tabIdx: Long = 1
- def selectTwit(idx: Int) {
- selectedTL = TwitterInfo.timeline(idx)
- tabIdx = 1
- selImg.icon = selectedTL.image
- selMsg.text = selectedTL.msg + " ["+ selectedTL.screenName + "]"
- selImg.repaint()
- }
- def selectReply(idx: Int) {
- selectedTL = TwitterInfo.replyline(idx)
- tabIdx = 2
- selImg.icon = selectedTL.image
- selMsg.text = selectedTL.msg + " ["+ selectedTL.screenName + "]"
- selImg.repaint()
- }
- def selectDM(idx: Int) {
- selectedTL = TwitterInfo.dmline(idx)
- tabIdx = 3
- selImg.icon = selectedTL.image
- selMsg.text = selectedTL.msg + " ["+ selectedTL.screenName + "]"
- selImg.repaint()
- }
- def open() {
- Swing.onEDT {
- val t = top
- SwingUtilities.updateComponentTreeUI(t.peer)
- if (t.size == new Dimension(0,0)) t.pack()
- t.visible = true
- }
- }
- def setVisible(flag: Boolean) {
- val t = top
- t.visible = flag
- }
- def setTimeLine(timeline: List[TimeLineInfo]) {
- val addedLines = TwitterInfo.addTimeLine(timeline)
- // addedLines.foreach(println(_))
- for (tl <- addedLines) {
- this.timeLine.model.insertRow(0, timelineToRow(tl))
- }
- while (this.timeLine.model.getRowCount > Consts.MAX_ROW) {
- this.timeLine.model.removeRow(this.timeLine.model.getRowCount-1)
- }
- TwitterInfo.timeline = TwitterInfo.timeline.take(Consts.MAX_ROW)
- }
- def setReplyLine(timeline: List[TimeLineInfo]) {
- val addedLines = TwitterInfo.addReplyLine(timeline)
- // addedLines.foreach(println(_))
- for (tl <- addedLines) {
- this.replyLine.model.insertRow(0, timelineToRow(tl))
- }
- while (this.replyLine.model.getRowCount > Consts.MAX_ROW) {
- this.replyLine.model.removeRow(this.replyLine.model.getRowCount-1)
- }
- TwitterInfo.replyline = TwitterInfo.replyline.take(Consts.MAX_ROW)
- }
- def setDMLine(timeline: List[TimeLineInfo]) {
- val addedLines = TwitterInfo.addDMLine(timeline)
- // addedLines.foreach(println(_))
- for (tl <- addedLines) {
- this.dmLine.model.insertRow(0, timelineToRow(tl))
- }
- while (this.dmLine.model.getRowCount > Consts.MAX_ROW) {
- this.dmLine.model.removeRow(this.dmLine.model.getRowCount-1)
- }
- TwitterInfo.dmline = TwitterInfo.dmline.take(Consts.MAX_ROW)
- }
- def updateTLInfo(id: Long, tl: TimeLineInfo) {
- val idx = TwitterInfo.timeline.indexWhere(_.id == id)
- if (idx >= 0) {
- TwitterInfo.timeline = TwitterInfo.timeline.updated(idx, tl)
- this.timeLine.model.removeRow(idx)
- this.timeLine.model.insertRow(idx, timelineToRow(tl))
- }
- }
- def updateDMInfo(id: Long, tl: TimeLineInfo) {
- val idx = TwitterInfo.dmline.indexWhere(_.id == id)
- if (idx >= 0) {
- TwitterInfo.dmline = TwitterInfo.dmline.updated(idx, tl)
- this.dmLine.model.removeRow(idx)
- this.dmLine.model.insertRow(idx, timelineToRow(tl))
- }
- }
- def updateReplyInfo(id: Long, tl: TimeLineInfo) {
- val idx = TwitterInfo.replyline.indexWhere(_.id == id)
- if (idx >= 0) {
- TwitterInfo.replyline = TwitterInfo.replyline.updated(idx, tl)
- this.replyLine.model.removeRow(idx)
- this.replyLine.model.insertRow(idx, timelineToRow(tl))
- }
- }
- val formatter = new java.text.SimpleDateFormat("HH:mm:ss yy/MM/dd")
- def timelineToRow(tl: TimeLineInfo): Array[AnyRef] = {
- Array[AnyRef](tl.image, tl.screenName, tl.msg, tl.relation, formatter.format(tl.time))
- }
- }
- class ImageIconLoader {
- var cache = MMap[URL, ImageIcon]()
- def load(url: URL): ImageIcon = {
- cache.get(url) match {
- case Some(imageIcon) => imageIcon
- case None =>
- var imageIcon = new ImageIcon(url)
- cache += (url -> imageIcon)
- cache(url)
- }
- }
- def clearCache() {
- cache.clear()
- }
- }
- object ImageIconLoader extends ImageIconLoader
- object TwitterInfo {
- var followIds = List[Long]()
- var followersIds = List[Long]()
- var lists = MMap[Int, List[Long]]()
- var userLists = MMap[Long, List[String]]()
- var user: User = null
- var timeline = List[TimeLineInfo]()
- var dmline = List[TimeLineInfo]()
- var replyline = List[TimeLineInfo]()
- def initialize(twitter: Twitter) {
- user = twitter.verifyCredentials()
- var ids1 = twitter.getFriendsIDs(-1)
- followIds :::= ids1.getIDs().toList
- var c1 = ids1.getNextCursor
- while (c1 != 0) {
- ids1 = twitter.getFriendsIDs(c1)
- followIds :::= ids1.getIDs().toList
- c1 = ids1.getNextCursor
- }
- var ids2 = twitter.getFollowersIDs(-1)
- followersIds :::= ids2.getIDs().toList
- var c2 = ids2.getNextCursor()
- while (c2 != 0) {
- ids2 = twitter.getFriendsIDs(c2)
- followersIds :::= ids2.getIDs().toList
- c2 = ids2.getNextCursor
- }
- def addUserList(userList: PagableResponseList[UserList]) {
- for (ul <- userList) {
- if (lists.contains(ul.getId)) {
- lists += (ul.getId -> (ul.getUser.getId :: lists(ul.getId)))
- } else {
- lists += (ul.getId -> List(ul.getUser.getId))
- }
- if (userLists.contains(ul.getUser.getId)) {
- userLists += (ul.getUser.getId -> (ul.getName :: userLists(ul.getUser.getId)))
- } else {
- userLists += (ul.getUser.getId -> List(ul.getName))
- }
- }
- }
- var ulCursor = -1L
- var userList = twitter.getUserLists(user.getId, ulCursor)
- addUserList(userList)
- while (userList.hasNext) {
- ulCursor = userList.getNextCursor
- userList = twitter.getUserLists(user.getId, ulCursor)
- addUserList(userList)
- }
- // -- debug
- // println("follow: " + followIds.mkString(","))
- // println("follower: " + followersIds.mkString(","))
- // println("list: " + lists)
- }
- def addTimeLine(newLines: List[TimeLineInfo]): List[TimeLineInfo] = {
- var returns = newLines.filter(tl => !this.timeline.exists(v => v.id == tl.id))
- for (tl <- returns) {
- this.timeline = tl :: this.timeline
- }
- returns
- }
- def addDMLine(newLines: List[TimeLineInfo]): List[TimeLineInfo] = {
- var returns = newLines.filter(tl => !this.dmline.exists(v => v.id == tl.id))
- for (tl <- returns) {
- this.dmline = tl :: this.dmline
- }
- returns
- }
- def addReplyLine(newLines: List[TimeLineInfo]): List[TimeLineInfo] = {
- var returns = newLines.filter(tl => !this.replyline.exists(v => v.id == tl.id))
- for (tl <- returns) {
- this.replyline = tl :: this.replyline
- }
- returns
- }
- def displayRelation(status: Status): String = {
- val user = status.getUser
- val isFollow = followIds.contains(user.getId)
- val isFollowed = followersIds.contains(user.getId)
- if (status.isRetweet) {
- "RT"
- } else if (status.isFavorited) {
- "?"
- } else if (user.isProtected) {
- "?"
- } else {
- if (isFollow && isFollowed) {
- "?"
- } else if (isFollow) {
- "?"
- } else if (isFollowed) {
- "?"
- } else {
- "?"
- }
- }
- }
- def displayListName(user: User): String = {
- if (userLists.contains(user.getId)) {
- userLists(user.getId).mkString("/")
- } else {
- "?"
- }
- }
- }
- class AuthDialog(_title: String, _message: String) extends Dialog {
- this.title = _title
- this.modal = true
- var result: Option[String] = None
- val pinField = new TextField
- contents = new BoxPanel(Orientation.Vertical) {
- contents += new EditorPane("text/html", _message.replaceAll("https?(:\\/\\/[-_.!~*\\'a-zA-Z0-9\\/?\\@&=+\\$%#]+)", "<a href='$0'>$0</a>")) {
- editable = false
- peer.putClientProperty(javax.swing.JEditorPane.HONOR_DISPLAY_PROPERTIES, java.lang.Boolean.TRUE)
- peer.setFont((new Label).font)
- peer.addHyperlinkListener(new HyperlinkHandler)
- }
- contents += pinField
- contents += new BoxPanel(Orientation.Horizontal) {
- contents += new scala.swing.Button {
- name = "Authorize"
- text = "??"
- reactions += {
- case ButtonClicked(b) =>
- result = Some(pinField.text)
- close()
- }
- }
- contents += new scala.swing.Button {
- name = "Cancel"
- text = "?????"
- reactions += {
- case ButtonClicked(b) =>
- result = None
- close()
- }
- }
- }
- }
- def getResult() = result
- }
- case class TimeLineInfo(id: Long, userID: Long, userName: String, screenName: String, msg: String, image: ImageIcon, time: Date, isRetweet: Boolean = false, isFavorited: Boolean = false, localtion: String = null, isProtected: Boolean = false, relation: String = null, listName: String = null, status: Status = null) {
- override def toString(): String = "@" + screenName + " - " + msg
- }
- class MGTwitter {
- private def statusToTimeLineInfo(status: Status): TimeLineInfo = {
- TimeLineInfo(
- id = status.getId,
- userID = status.getUser.getId,
- userName = status.getUser.getName,
- screenName = status.getUser.getScreenName,
- msg = status.getText,
- image = ImageIconLoader.load(status.getUser.getProfileImageUrlHttps),
- time = status.getCreatedAt,
- relation = TwitterInfo.displayRelation(status),
- listName = TwitterInfo.displayListName(status.getUser),
- isProtected = status.getUser.isProtected,
- status = status
- )
- }
- private def messageToTimeLineInfo(status: DirectMessage): TimeLineInfo = {
- TimeLineInfo(
- id = status.getId,
- userID = status.getSender.getId,
- userName = status.getSender.getName,
- screenName = status.getSender.getScreenName,
- msg = status.getText,
- image = ImageIconLoader.load(status.getSender.getProfileImageUrlHttps),
- time = status.getCreatedAt,
- relation = "?",
- listName = TwitterInfo.displayListName(status.getSender),
- isProtected = status.getSender.isProtected
- )
- }
- val twitter = login()
- TwitterInfo.initialize(twitter)
- def twit(text: String) {
- twitter.updateStatus(text)
- }
- def twit(status: StatusUpdate) {
- twitter.updateStatus(status)
- }
- def retweet(statusID: Long) {
- twitter.retweetStatus(statusID)
- }
- def fav(statusID: Long) {
- twitter.createFavorite(statusID)
- }
- def sendDirectMessage(userID: Long, text: String) {
- twitter.sendDirectMessage(userID, text)
- }
- def getReplyLine(): List[TimeLineInfo] = {
- var timelines = List[TimeLineInfo]()
- try {
- val statuses = twitter.getMentions(new Paging(1, 20))
- for (status <- statuses) {
- timelines ::= statusToTimeLineInfo(status)
- }
- } catch {
- case te: Exception =>
- te.printStackTrace()
- //Dialog.showMessage(title="ERROR", message="???????????????: " + te.getMessage)
- }
- timelines
- }
- def getDMLine(): List[TimeLineInfo] = {
- var timelines = List[TimeLineInfo]()
- try {
- val statuses = twitter.getDirectMessages(new Paging(1, 20))
- for (status <- statuses) {
- timelines ::= messageToTimeLineInfo(status)
- }
- } catch {
- case te: Exception =>
- te.printStackTrace()
- //Dialog.showMessage(title="ERROR", message="DM???????????: " + te.getMessage)
- }
- timelines
- }
- def getHomeTimeLine(): List[TimeLineInfo] = {
- var timelines = List[TimeLineInfo]()
- try {
- val statuses = twitter.getHomeTimeline(new Paging(1, 100))
- for (status <- statuses) {
- timelines ::= statusToTimeLineInfo(status)
- }
- } catch {
- case te: Exception =>
- te.printStackTrace()
- //Dialog.showMessage(title="ERROR", message="?????????????????: " + te.getMessage)
- }
- timelines
- }
- def login(): Twitter = {
- val tokenFile = new File("token")
- if (tokenFile.exists) {
- val config = ConfigLoader.load("token")
- val accessToken = new AccessToken(config("token").toString, config("tokenSecret").toString)
- new TwitterFactory().getInstance(accessToken)
- } else {
- val tw = new TwitterFactory().getInstance()
- // ?????????????????????????????
- val requestToken = tw.getOAuthRequestToken()
- var accessToken: AccessToken = null
- val br = new BufferedReader(new InputStreamReader(System.in))
- while (null == accessToken) {
- // val optPin = Dialog.showInput(
- // message="??URL???????????PIN????????????: \n"+requestToken.getAuthorizationURL(),
- // initial="")
- val dialog = new AuthDialog(
- "??",
- "??URL???????????PIN????????????: <br>"+requestToken.getAuthorizationURL())
- dialog.open()
- val optPin = dialog.getResult
- optPin match {
- case Some(pin) =>
- try{
- if(pin.length() > 0){
- accessToken = tw.getOAuthAccessToken(requestToken, pin)
- }else{
- accessToken = tw.getOAuthAccessToken()
- }
- } catch {
- case te: TwitterException =>
- if(401 == te.getStatusCode()){
- Dialog.showMessage(
- title="ERROR",
- message="??????????")
- }else{
- te.printStackTrace()
- }
- case e: Exception => e.printStackTrace()
- }
- case _ =>
- Dialog.showMessage(
- title="?????",
- message="??????????????")
- System.exit(0)
- }
- }
- //??????? accessToken ??????
- ConfigLoader.save("token", Map("token" -> accessToken.getToken, "tokenSecret" -> accessToken.getTokenSecret))
- tw
- }
- }
- }
- object Megane {
- lazy val mgtw = new MGTwitter
- def mgTwitter: MGTwitter = mgtw
- }
- class Megane {
- var window: MGTWindow = new MGTWindow
- def execute(args: Array[String]) {
- val mgtw = Megane.mgTwitter
- // ??????????
- val image = ImageIO.read(classOf[Megane].getResourceAsStream("icon.png"))
- val icon = new TrayIcon(image)
- icon.addActionListener(new ActionListener {
- def actionPerformed(e: ActionEvent) {
- window.setTimeLine(mgtw.getHomeTimeLine())
- window.setDMLine(mgtw.getDMLine())
- window.setReplyLine(mgtw.getReplyLine())
- window.setVisible(true)
- }
- })
- window.open()
- window.setTimeLine(mgtw.getHomeTimeLine())
- window.setDMLine(mgtw.getDMLine())
- window.setReplyLine(mgtw.getReplyLine())
- window.setVisible(true)
- // ??????????
- val menu = new PopupMenu
- // ??????
- val exitItem = new MenuItem("??")
- exitItem.addActionListener(new ActionListener {
- def actionPerformed(e: ActionEvent) {
- System.exit(0)
- }
- })
- // ????????????????
- menu.add(exitItem)
- icon.setPopupMenu(menu)
- // ?????????
- SystemTray.getSystemTray().add(icon)
- var count = 0
- val task = new TimerTask {
- def run() {
- window.setTimeLine(mgtw.getHomeTimeLine())
- if (count % 10 == 0) {
- //???DM??
- window.setDMLine(mgtw.getDMLine())
- window.setReplyLine(mgtw.getReplyLine())
- }
- count += 1
- }
- }
- val timer = new Timer
- timer.scheduleAtFixedRate(task, 0, 1 * 60 * 1000)
- }
- }
- //class MMLPlayer {
- // def play() {}
- //}
- //class MMLParser {
- // def parse(text: String) : MMLPlayer = {
- // }
- //}
- object Main {
- private var OS = 'Windows
- private var JVM_VERSION = "1.6"
- def checkOSAndJVMVersion(): Boolean = {
- var osname = System.getProperty("os.name")
- var jvmVersion = System.getProperty("java.version")
- // println("OS Name = " + osname)
- osname = osname.toLowerCase
- // println("Java Version = " + jvmVersion)
- var appendMessages = List("???JDK?JRE?????????????", "http://www.oracle.com/technetwork/java/javase/downloads")
- if (osname.startsWith("mac os x")) {
- OS = 'Mac
- appendMessages = List("Mac OS X 10.5 ? Intel Core2Duo ???????", "?Java Preferences????? Java SE 6 ??????????")
- } else if (osname.startsWith("windows")) {
- OS = 'Windows
- } else if (osname.startsWith("linux")) {
- OS = 'Linux
- } else {
- println("unknown os! name = " + osname)
- }
- if (jvmVersion < "1.6") {
- println("Java??????????????????????????\n"
- +"java version = " + jvmVersion + ", (" +appendMessages.mkString(",")+")")
- return false
- }
- JVM_VERSION = jvmVersion
- true
- }
- val megane = new Megane
- def main(args: Array[String]) {
- checkOSAndJVMVersion
- if (OS == 'Windows) {
- UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel")
- }
- megane.execute(args)
- }
- }