| 1 | // |
|---|
| 2 | // hatebuWatchDog.js - hatena bookmark watch dog - |
|---|
| 3 | // |
|---|
| 4 | // LICENSE: {{{ |
|---|
| 5 | // |
|---|
| 6 | // This software distributable under the terms of an MIT-style license. |
|---|
| 7 | // |
|---|
| 8 | // Copyright (c) 2009 snaka<snaka.gml@gmail.com> |
|---|
| 9 | // |
|---|
| 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
|---|
| 11 | // of this software and associated documentation files (the "Software"), to deal |
|---|
| 12 | // in the Software without restriction, including without limitation the rights |
|---|
| 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|---|
| 14 | // copies of the Software, and to permit persons to whom the Software is |
|---|
| 15 | // furnished to do so, subject to the following conditions: |
|---|
| 16 | // |
|---|
| 17 | // The above copyright notice and this permission notice shall be included in |
|---|
| 18 | // all copies or substantial portions of the Software. |
|---|
| 19 | // |
|---|
| 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|---|
| 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|---|
| 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|---|
| 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|---|
| 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|---|
| 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|---|
| 26 | // THE SOFTWARE. |
|---|
| 27 | // |
|---|
| 28 | // OSI page : http://opensource.org/licenses/mit-license.php |
|---|
| 29 | // Japanese : http://sourceforge.jp/projects/opensource/wiki/licenses%2FMIT_license |
|---|
| 30 | // |
|---|
| 31 | // }}} |
|---|
| 32 | // PLUGIN INFO: {{{ |
|---|
| 33 | let PLUGIN_INFO = |
|---|
| 34 | <VimperatorPlugin> |
|---|
| 35 | <name>{NAME}</name> |
|---|
| 36 | <description>Make notify hatebu-count when specified site's hatebu-count changed.</description> |
|---|
| 37 | <description lang="ja">指定されたサイトのはてブ数を監視、変動があったらお知らせします。</description> |
|---|
| 38 | <minVersion>2.0pre</minVersion> |
|---|
| 39 | <maxVersion>2.2pre</maxVersion> |
|---|
| 40 | <updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/hatebuWatchDog.js</updateURL> |
|---|
| 41 | <author mail="snaka.gml@gmail.com" homepage="http://vimperator.g.hatena.ne.jp/snaka72/">snaka</author> |
|---|
| 42 | <license>MIT style license</license> |
|---|
| 43 | <version>1.2.0</version> |
|---|
| 44 | <detail><![CDATA[ |
|---|
| 45 | == Subject == |
|---|
| 46 | Make notify hatebu-count when specified site's hatebu-count changed. |
|---|
| 47 | Usage is just put this script into vimperator's plugin directory. |
|---|
| 48 | |
|---|
| 49 | == Global variables == |
|---|
| 50 | g:hatebuWatchDogInterval: |
|---|
| 51 | Number. Watching interval. Default:600 Min:60 |
|---|
| 52 | g:hatebuWtachDogTargets: |
|---|
| 53 | String. Sites where it wants you to watch. |
|---|
| 54 | If you want watch only one site, you should specify like following. |
|---|
| 55 | >|| |
|---|
| 56 | :let g:hatebuWatchDogTargets = "http://d.hatena.ne.jp/snaka72/" |
|---|
| 57 | ||< |
|---|
| 58 | If you want watch more than one site, you should specify like following. |
|---|
| 59 | >|| |
|---|
| 60 | :let g:hatebuWatchDogTargets = "['http://d.hatena.ne.jp/snaka72/', 'http://vimperator.g.hatena.ne.jp/snaka72/']" |
|---|
| 61 | ||< |
|---|
| 62 | g:hatebuWatchDogAlways: |
|---|
| 63 | Boole. Make notify every time. (for debug) Default:false |
|---|
| 64 | |
|---|
| 65 | ]]></detail> |
|---|
| 66 | <detail lang="ja"><![CDATA[ |
|---|
| 67 | == 概要 == |
|---|
| 68 | 指定されたサイトの被はてブ数を監視して、その数値に変動があったらお知らせします。 |
|---|
| 69 | 使い方は、このスクリプトをVimperatorのpluginディレクトリに格納するだけです。 |
|---|
| 70 | |
|---|
| 71 | == グローバル変数 == |
|---|
| 72 | g:hatebuWatchDogInterval: |
|---|
| 73 | Number. 監視の間隔(秒). デフォルト600 設定可能な最小値:60 |
|---|
| 74 | g:hatebuWtachDogTargets: |
|---|
| 75 | String. Sites where it wants you to watch |
|---|
| 76 | 監視対象のサイトが一つだけの場合は以下のように設定します。 |
|---|
| 77 | >|| |
|---|
| 78 | :let g:hatebuWatchDogTargets = "http://d.hatena.ne.jp/snaka72/" |
|---|
| 79 | ||< |
|---|
| 80 | 監視対象のサイトがが複数の場合は以下のように設定します。 |
|---|
| 81 | >|| |
|---|
| 82 | :let g:hatebuWatchDogTargets = "['http://d.hatena.ne.jp/snaka72/', 'http://vimperator.g.hatena.ne.jp/snaka72/']" |
|---|
| 83 | ||< |
|---|
| 84 | g:hatebuWatchDogAlways: |
|---|
| 85 | Boole. 毎回報告を挙げるかどうか。デフォルト:false (主にでバッグ用) |
|---|
| 86 | |
|---|
| 87 | == ToDo == |
|---|
| 88 | - 新着ブックマークのユーザidとコメントの表示 |
|---|
| 89 | |
|---|
| 90 | ]]></detail> |
|---|
| 91 | </VimperatorPlugin>; |
|---|
| 92 | // }}} |
|---|
| 93 | |
|---|
| 94 | // Clear all watchers if started watcher exists. |
|---|
| 95 | if (plugins.hatebuWatchDog && plugins.hatebuWatchDog.stopWatching) |
|---|
| 96 | plugins.hatebuWatchDog.stopWatching(); |
|---|
| 97 | |
|---|
| 98 | let publics = plugins.hatebuWatchDog = (function() { |
|---|
| 99 | // PRIVATE //////////////////////////////////////////////////////////////{{{ |
|---|
| 100 | const libly = plugins.libly; |
|---|
| 101 | let previousValue = 0; |
|---|
| 102 | let tasks = []; |
|---|
| 103 | |
|---|
| 104 | function getCurrentValue(target, onSuccess, onFailure) { |
|---|
| 105 | // build hatebu xml-rpc request |
|---|
| 106 | let req = new libly.Request( |
|---|
| 107 | 'http://b.hatena.ne.jp/xmlrpc', |
|---|
| 108 | { |
|---|
| 109 | 'Content-Type' : 'text/xml' |
|---|
| 110 | },{ |
|---|
| 111 | postBody : <methodCall> |
|---|
| 112 | <methodName>bookmark.getTotalCount</methodName> |
|---|
| 113 | <params> |
|---|
| 114 | <param><value><string>{target}</string></value></param> |
|---|
| 115 | </params> |
|---|
| 116 | </methodCall>.toXMLString() |
|---|
| 117 | } |
|---|
| 118 | ); |
|---|
| 119 | |
|---|
| 120 | let currentValue; |
|---|
| 121 | req.addEventListener("onSuccess", function(data) { |
|---|
| 122 | liberator.log("XML-RPC request was succeeded."); |
|---|
| 123 | let resXml = new XML(data.responseText.replace(/^<\?xml version[^>]+?>/, '')); |
|---|
| 124 | currentValue = window.eval(resXml..int.toString()); |
|---|
| 125 | onSuccess(currentValue); |
|---|
| 126 | }); |
|---|
| 127 | req.addEventListener("onFailure", function(data) { |
|---|
| 128 | onFailure(); |
|---|
| 129 | }); |
|---|
| 130 | liberator.log("reauest..."); |
|---|
| 131 | req.post(); |
|---|
| 132 | liberator.log("done..."); |
|---|
| 133 | } |
|---|
| 134 | |
|---|
| 135 | function notifyAlways() |
|---|
| 136 | window.eval(liberator.globalVariables.hatebuWatchDogAlways) || false; |
|---|
| 137 | |
|---|
| 138 | function showHatebuNotification(targetSite, currentValue, delta) { |
|---|
| 139 | let title = delta >= 0 |
|---|
| 140 | ? "hatebuWatchDog\u304B\u3089\u306E\u304A\u77E5\u3089\u305B" // ordinary notification |
|---|
| 141 | : "\u6B8B\u5FF5\u306A\u304A\u77E5\u3089\u305B" // bad notification |
|---|
| 142 | let suffix = delta != 0 ? "\u306B\u306A\u308A\u307E\u3057\u305F\u3002" |
|---|
| 143 | : "\u3067\u3059\u3002"; |
|---|
| 144 | let message = "'" + targetSite + "' \u306E\u88AB\u306F\u3066\u30D6\u6570\u306F '" + |
|---|
| 145 | currentValue + "' " + suffix + " (" + getSignedNum(delta) + ")"; |
|---|
| 146 | |
|---|
| 147 | //showAlertNotification(null, title, message); |
|---|
| 148 | //publics.notify(title, message); |
|---|
| 149 | (getNotifier())(title, message); |
|---|
| 150 | } |
|---|
| 151 | |
|---|
| 152 | function getSignedNum(num) { |
|---|
| 153 | if (num > 0) return "+" + num; |
|---|
| 154 | if (num < 0) return "-" + Math.abs(num); |
|---|
| 155 | return "0"; |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | let _notifier = null; |
|---|
| 159 | const GROWL_EXTENSION_ID = "growlgntp@brian.dunnington"; |
|---|
| 160 | |
|---|
| 161 | function getNotifier() { |
|---|
| 162 | if (_notifier) return _notifier; |
|---|
| 163 | |
|---|
| 164 | if (Application.extensions.has(GROWL_EXTENSION_ID) && |
|---|
| 165 | Application.extensions.get(GROWL_EXTENSION_ID).enabled) { |
|---|
| 166 | _notifier = publics.notify; |
|---|
| 167 | } |
|---|
| 168 | else { |
|---|
| 169 | _notifier = showAlertNotification; |
|---|
| 170 | } |
|---|
| 171 | return _notifier; |
|---|
| 172 | } |
|---|
| 173 | |
|---|
| 174 | function showAlertNotification(title, message) { |
|---|
| 175 | Cc['@mozilla.org/alerts-service;1'] |
|---|
| 176 | .getService(Ci.nsIAlertsService) |
|---|
| 177 | .showAlertNotification( |
|---|
| 178 | null, //'chrome://mozapps/skin/downloads/downloadIcon.png', |
|---|
| 179 | title, |
|---|
| 180 | message |
|---|
| 181 | ); |
|---|
| 182 | } |
|---|
| 183 | |
|---|
| 184 | function growl() Components.classes['@growlforwindows.com/growlgntp;1'] |
|---|
| 185 | .getService().wrappedJSObject; |
|---|
| 186 | const growlIcon = "http://img.f.hatena.ne.jp/images/fotolife/s/snaka72/20090608/20090608082837.png"; // temporary |
|---|
| 187 | |
|---|
| 188 | function growlRegister() { |
|---|
| 189 | growl().register( |
|---|
| 190 | PLUGIN_INFO.name, |
|---|
| 191 | growlIcon, |
|---|
| 192 | [ |
|---|
| 193 | {name: 'announce', displayName: 'Announce from hatebuWatchDog'}, |
|---|
| 194 | {name: 'sadlynews',displayName: 'Sadly announce from hatebuWatchdog'}, |
|---|
| 195 | {name: 'failed', displayName: 'Erroer report from hatebuWatchdog'} |
|---|
| 196 | ] |
|---|
| 197 | ); |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | function getInterval() |
|---|
| 201 | window.eval(liberator.globalVariables.hatebuWatchDogInterval) || 600; // default : 10 min. |
|---|
| 202 | |
|---|
| 203 | // for debug |
|---|
| 204 | let log = liberator.log; |
|---|
| 205 | let dump = liberator.dump; |
|---|
| 206 | |
|---|
| 207 | // }}} |
|---|
| 208 | // PUBLIC ///////////////////////////////////////////////////////////////{{{ |
|---|
| 209 | let self = { |
|---|
| 210 | startWatching: function() { |
|---|
| 211 | let targets; |
|---|
| 212 | try { |
|---|
| 213 | targets = window.eval(liberator.globalVariables.hatebuWatchDogTargets); |
|---|
| 214 | } catch(e) { |
|---|
| 215 | targets = liberator.globalVariables.hatebuWatchDogTargets; |
|---|
| 216 | } |
|---|
| 217 | if (targets) { |
|---|
| 218 | if (!(targets instanceof Array)) |
|---|
| 219 | targets = [targets]; |
|---|
| 220 | let i = 1, delay = 5000; |
|---|
| 221 | log("before setTimeout()"); |
|---|
| 222 | targets.forEach(function(targetSite) { |
|---|
| 223 | setTimeout(function() { |
|---|
| 224 | publics.addTask({site : targetSite}); |
|---|
| 225 | }, delay * i++); |
|---|
| 226 | }); |
|---|
| 227 | log("after setTimeout()"); |
|---|
| 228 | } |
|---|
| 229 | else { |
|---|
| 230 | liberator.echoerr("Please set g:hatebeWatchDogTargets before watching()."); |
|---|
| 231 | } |
|---|
| 232 | }, |
|---|
| 233 | |
|---|
| 234 | addTask: function(target) { |
|---|
| 235 | dump(target.site); |
|---|
| 236 | const MINUTE = 60; // sec. |
|---|
| 237 | interval = getInterval() || (10 * MINUTE); // default 10 min. |
|---|
| 238 | interval = Math.max(interval, MINUTE); // lower limt is 1 min. |
|---|
| 239 | |
|---|
| 240 | // initialize previous value |
|---|
| 241 | target.previousValue = 0; |
|---|
| 242 | target.initialize = true; |
|---|
| 243 | publics.watching(target); |
|---|
| 244 | |
|---|
| 245 | // set watching interval |
|---|
| 246 | tasks.push(setInterval(publics.watching, 1000 * interval, target)); |
|---|
| 247 | dump({target: target, interval: interval}); |
|---|
| 248 | }, |
|---|
| 249 | |
|---|
| 250 | clearAllTasks: function() { |
|---|
| 251 | tasks.forEach(function(task) { |
|---|
| 252 | clearInterval(task); |
|---|
| 253 | }); |
|---|
| 254 | tasks = []; |
|---|
| 255 | dump("watch dog is sleeping..."); |
|---|
| 256 | }, |
|---|
| 257 | |
|---|
| 258 | watching: function(target) { |
|---|
| 259 | dump("watching..."); |
|---|
| 260 | dump(target); |
|---|
| 261 | |
|---|
| 262 | getCurrentValue( |
|---|
| 263 | target.site, |
|---|
| 264 | function(currentValue) { |
|---|
| 265 | if (target.initialize) { |
|---|
| 266 | target.initialize = false; |
|---|
| 267 | target.previousValue = currentValue; |
|---|
| 268 | return; |
|---|
| 269 | } |
|---|
| 270 | let delta = currentValue - target.previousValue; |
|---|
| 271 | if (delta || notifyAlways()) { |
|---|
| 272 | showHatebuNotification(target.site, currentValue, delta); |
|---|
| 273 | } |
|---|
| 274 | target.previousValue = currentValue; |
|---|
| 275 | }, |
|---|
| 276 | function() { |
|---|
| 277 | liberator.echoerr("Cannot get current value."); |
|---|
| 278 | } |
|---|
| 279 | ); |
|---|
| 280 | }, |
|---|
| 281 | |
|---|
| 282 | notify: function(title, message) { |
|---|
| 283 | growlRegister(); |
|---|
| 284 | growl().notify( |
|---|
| 285 | PLUGIN_INFO.name, |
|---|
| 286 | 'announce', |
|---|
| 287 | title, |
|---|
| 288 | message |
|---|
| 289 | ); |
|---|
| 290 | } |
|---|
| 291 | }; |
|---|
| 292 | // }}} |
|---|
| 293 | return self; |
|---|
| 294 | })(); |
|---|
| 295 | |
|---|
| 296 | // Awaking the watch dog. |
|---|
| 297 | publics.startWatching(); |
|---|
| 298 | liberator.dump("Watch dog is awaking ..."); |
|---|
| 299 | // vim: sw=2 ts=2 et fdm=marker |
|---|