Advertisement
AndrzejL

rssnews.tcl

Nov 3rd, 2011
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.98 KB | None | 0 0
  1. #######################################################################
  2. # #
  3. # rssnews.tcl - RSS news announcer for eggdrop by demond@demond.net #
  4. # #
  5. # this will announce the updated news from RSS feed(s), #
  6. # periodically polling the feed(s); supports multiple #
  7. # channels/multiple feeds per channel; you only need to #
  8. # set up your feeds array, see below; secure (SSL) and #
  9. # private (password-protected) feeds are also supported #
  10. # #
  11. # Usage: !news <feed name> [news index #] - from channel #
  12. # .rss <add|del|list> [name:#chan] - from partyline #
  13. # #
  14. #######################################################################
  15.  
  16. package require Tcl 8.3
  17. package require eggdrop 1.6
  18. package require http 2.0
  19.  
  20. namespace eval rssnews {
  21.  
  22. # set your feed(s) sources here: feed name, channel, poll frequency in mins, feed URL
  23. #
  24. set feeds(google:#pclinuxos-pl) {1 http://news.google.com/news?ned=us&topic=h&output=rss}
  25. #set feeds(niebezpiecznik:#pclinuxos-pl) {1 http://feeds.feedburner.com/niebezpiecznik/}
  26. #set feeds(PCLinuxOS_Strona_Glowna:#pclinuxos-pl) {60 http://pclinuxos.com/?feed=rss2}
  27. #set feeds(PCLinuxOS_Polska_Sekcja:#pclinuxos-pl) {1 http://www.pclinuxos.com/forum/index.php?#board=15.0;type=rss;action=.xml;limit=10}
  28. #set feeds(PCLinuxOS_Forum_Polska_Sekcja:#pclinuxos-pl) {20 http://www.pclinuxos.com/forum/index.php?board=15.0;type=rss;action=.xml;limit=10}
  29. #set feeds(PCLinuxOS_Polskie_Forum:#pclinuxos-pl) {20 http://forum.pclinuxos.org.pl/feed.php}
  30. #set feeds(SecurityNow_Podcast:#pclinuxos-pl) {60 http://leoville.tv/podcasts/sn.xml}
  31. #set feeds(Niebezpiecznik_Glowna:#pclinuxos-pl) {5 http://feeds.feedburner.com/niebezpiecznik/}
  32. #set feeds(Niebezpiecznik_LinkBlog:#pclinuxos-pl) {5 http://feeds.feedburner.com/niebezpiecznik/linkblog/}
  33. #set feeds(JoeMonster_Krotkie_Dowcipy:#pclinuxos-pl) {60 http://www.joemonster.org/backend.php?channel=krotkie}
  34. #set feeds(PCLinuxOS_Polska_Sekcja:#pclinuxos-pl) {11 http://www.pclinuxos.com/forum/index.php?board=15.0;type=rss;action=.xml;limit=50}
  35. #
  36. # if you have to use password-protected feed, set it up like this:
  37. #
  38. #set feeds(name3:#chan3) {13 http://some.site.com/feed username password}
  39.  
  40. # maximum number of announced new headlines
  41. #
  42. variable maxnew 5
  43.  
  44. # feed fetch timeout in seconds
  45. #
  46. variable timeout 120
  47.  
  48. # public trigger flood settings
  49. #
  50. variable pubflud 5:15
  51.  
  52. # support SSL feeds (requires TLS package)
  53. #
  54. variable usessl 0
  55.  
  56. # if usessl is 1, request/require valid certificate from server
  57. #
  58. variable reqcert yes:no
  59.  
  60. #######################################################################
  61. # nothing to edit below
  62.  
  63. variable version "rssnews-2.2"
  64.  
  65. if {$usessl} {
  66. package require tls 1.5
  67. scan $reqcert {%[^:]:%s} r1 r2
  68. if {$r1 == "yes"} {set r1 1} {set r1 0}
  69. if {$r2 == "yes"} {set r2 1} {set r2 0}
  70. set ssl [list ::tls::socket -request $r1 -require $r2]
  71. ::http::register https 443 $ssl
  72. }
  73.  
  74. bind dcc m rss [namespace current]::rss
  75. bind pub - !news [namespace current]::news
  76. bind time - * [namespace current]::timer
  77.  
  78. putlog "$version by demond loaded"
  79.  
  80. proc timer {min hour day month year} {
  81. variable feeds
  82. if {[info exists feeds]} {
  83. set mins [expr [scan $min %d]+[scan $hour %d]*60]
  84. foreach {chanfeed settings} [array get feeds] {
  85. if {$mins % [lindex $settings 0] == 0} {
  86. if {[llength $settings] > 2} {
  87. foreach {t url user pass} $settings {break}
  88. fetch $url $chanfeed $user $pass
  89. } {
  90. foreach {t url} $settings {break}
  91. fetch $url $chanfeed
  92. }
  93. }
  94. }}
  95. }
  96.  
  97. proc fetch {url chanfeed args} {
  98. variable timeout
  99. variable version; variable token
  100. set to [expr {$timeout * 1000}]
  101. set cmd [namespace current]::callback
  102. if {[llength $args] > 0} {
  103. foreach {user pass} $args {break}
  104. set hdr [list Authorization "Basic [b64en $user:$pass]"]
  105. } { set hdr {}}
  106. ::http::config -useragent "$version by demond"
  107. if {[catch {set t [::http::geturl $url -command $cmd -timeout $to -headers $hdr]} err]} {
  108. putlog "$version: ERROR($chanfeed): $err"
  109. } {
  110. set token($t) [list $url $chanfeed $args]
  111. }
  112. }
  113.  
  114. proc callback {t} {
  115. variable version; variable token
  116. foreach {url chanfeed args} $token($t) {break}
  117. switch -exact [::http::status $t] {
  118. "timeout" {
  119. putlog "$version: ERROR($chanfeed): timeout"
  120. }
  121. "error" {
  122. putlog "$version: ERROR($chanfeed): [::http::error $t]"
  123. }
  124. "ok" {
  125. switch -glob [::http::ncode $t] {
  126. 3* {
  127. upvar #0 $t state
  128. array set meta $state(meta)
  129. fetch $meta(Location) $chanfeed $args
  130. }
  131. 200 {
  132. process [::http::data $t] $chanfeed
  133. }
  134. default {
  135. putlog "$version: ERROR($chanfeed): [::http::code $t]"
  136. }}
  137. }
  138. default {
  139. putlog "$version: ERROR($chanfeed): got EOF from socket"
  140. }}
  141. ::http::cleanup $t
  142. }
  143.  
  144. proc process {data chanfeed} {
  145. variable news; variable hash
  146. variable maxnew; variable source
  147. set idx 1; set count 0
  148. scan $chanfeed {%[^:]:%s} feed chan
  149. set news($chanfeed) {}; set source($chanfeed) ""
  150. if {[regexp {(?i)<title>(.*?)</title>} $data -> foo]} {
  151. append source($chanfeed) $foo
  152. }
  153. if {[regexp {(?i)<description>(.*?)</description>} $data -> foo]} {
  154. append source($chanfeed) " | $foo"
  155. }
  156. set infoline $source($chanfeed)
  157. regsub -all {(?i)<items.*?>.*?</items>} $data {} data
  158. foreach {foo item} [regexp -all -inline {(?i)<item.*?>(.*?)</item>} $data] {
  159. regexp {(?i)<title.*?>(.*?)</title>} $item -> title
  160. regexp {(?i)<link.*?>(.*?)</link} $item -> link
  161. regexp {(?i)<desc.*?>(.*?)</desc.*?>} $item -> descr
  162. if {![info exists title]} {set title "(none)"}
  163. if {![info exists link]} {set link "(none)"}
  164. if {![info exists descr]} {set descr "(none)"}
  165. strip title link descr
  166. if {[info exists hash($chanfeed)]} {
  167. if {[lsearch -exact $hash($chanfeed) [md5 $title]] == -1 && [botonchan $chan]} {
  168. if {![info exists header]} {
  169. if {$infoline == ""} {set header $feed} {set header $infoline}
  170. puthelp "privmsg $chan :\002Breaking news\002 from $header!"
  171. }
  172. if {$count < $maxnew} {
  173. puthelp "privmsg $chan :($idx) $title"
  174. incr count
  175. } {
  176. lappend indices $idx
  177. }
  178. }}
  179. lappend news($chanfeed) [list $title $link $descr]
  180. lappend hashes [md5 $title]
  181. incr idx
  182. }
  183. if {[info exists indices] && [botonchan $chan]} {
  184. set count [llength $indices]
  185. set indices "(indices: [join $indices {, }])"
  186. puthelp "privmsg $chan :...and $count more $indices"
  187. }
  188. set hash($chanfeed) $hashes
  189. }
  190.  
  191. proc strip {args} {
  192. variable html
  193. foreach a $args {
  194. upvar $a x
  195. set amp {&amp; &}
  196. set x [string map $amp $x]
  197. set x [string map $html $x]
  198. while {[regexp -indices {(&#[0-9]{1,3};)} $x -> idxs]} {
  199. set b [lindex $idxs 0]; set e [lindex $idxs 1]
  200. set num [string range $x [expr {$b+2}] [expr {$e-1}]]
  201. if {$num < 256} {
  202. set x [string replace $x $b $e [format %c $num]]
  203. }
  204. }
  205. regexp {(?i)<!\[CDATA\[(.*?)\]\]>} $x -> x
  206. regsub -all {(?i)</t[dr]><t[dr].*?>} $x { | } x
  207. regsub -all {(?i)(<p>|<br>|\n)} $x { } x
  208. regsub -all {<[^<]+?>} $x {} x
  209. }
  210. }
  211.  
  212. proc rss {hand idx text} {
  213. variable feeds
  214. if {$text == ""} {
  215. putdcc $idx "Usage: .$::lastbind <add|del|list> \[name:#chan \[feed\]\]"
  216. return
  217. }
  218. set text [split $text]
  219. switch [lindex $text 0] {
  220. "list" {
  221. if {[info exists feeds]} {
  222. foreach {chanfeed settings} [array get feeds] {
  223. putdcc $idx "$chanfeed -> [join $settings]"
  224. }}
  225. }
  226. "add" {
  227. if {[llength $text] < 4} {
  228. putdcc $idx "not enough add arguments"
  229. return
  230. }
  231. set chanfeed [lindex $text 1]
  232. if {[info exists feeds]} {
  233. set names [string tolower [array names feeds]]
  234. if {[lsearch -exact $names [string tolower $chanfeed]] != -1} {
  235. putdcc $idx "$chanfeed already exists"
  236. return
  237. }}
  238. set feeds($chanfeed) [lrange $text 2 end]
  239. }
  240. "del" {
  241. set chanfeed [lindex $text 1]
  242. if {[info exists feeds]} {
  243. set names [string tolower [array names feeds]]
  244. if {[lsearch -exact $names [string tolower $chanfeed]] == -1} {
  245. putdcc $idx "$chanfeed does not exist"
  246. return
  247. } {
  248. unset feeds($chanfeed)
  249. }}
  250. }
  251. default {
  252. putdcc $idx "invalid sub-command"
  253. return
  254. }
  255.  
  256. }
  257. return 1
  258. }
  259.  
  260. proc news {nick uhost hand chan text} {
  261. variable source
  262. variable news; variable feeds
  263. variable pcount; variable pubflud
  264. if {[info exists pcount]} {
  265. set n [lindex $pcount 1]; incr n
  266. set ts [lindex $pcount 0]
  267. set pcount [list $ts $n]
  268. scan $pubflud {%[^:]:%s} maxr maxt
  269. if {$n >= $maxr} {
  270. if {[unixtime] - $ts <= $maxt} {return}
  271. set n 1; set ts [unixtime]
  272. }
  273. } {
  274. set n 1; set ts [unixtime]
  275. }
  276. set pcount [list $ts $n]
  277. set num [lindex [split $text] 1]
  278. set feed [lindex [split $text] 0]
  279. if {$text == ""} {
  280. foreach {key value} [array get feeds] {
  281. scan $key {%[^:]:%s} name channel
  282. if {[string eq -noc $chan $channel]} {
  283. lappend names $name
  284. }
  285. }
  286. if {[info exists names]} {
  287. set names [join $names {, }]
  288. puthelp "notice $nick :feed(s) for $chan: $names"
  289. puthelp "notice $nick :type $::lastbind <feed> \[index#\]"
  290. } {
  291. puthelp "notice $nick :no feed(s) for $chan"
  292. }
  293. return 1
  294. }
  295. if {![info exists news($feed:$chan)]} {
  296. puthelp "notice $nick :no news from $feed on $chan"
  297. return 1
  298. }
  299. if {$num == ""} {
  300. set idx 1
  301. if {$source($feed:$chan) != ""} {
  302. set title $source($feed:$chan)
  303. } {
  304. set title [lindex $feeds($feed:$chan) 1]
  305. }
  306. puthelp "notice $nick :News source: \002$title\002"
  307. foreach item $news($feed:$chan) {
  308. puthelp "notice $nick :($idx) [lindex $item 0]"
  309. incr idx
  310. }
  311. return 1
  312. } elseif {![string is integer $num]} {
  313. puthelp "notice $nick :news index must be number"
  314. return 1
  315. }
  316. if {$num < 1 || $num > [llength $news($feed:$chan)]} {
  317. puthelp "notice $nick :no such news index, try $::lastbind $feed"
  318. return 1
  319. } {
  320. set idx [expr {$num-1}]
  321. puthelp "notice $nick :......title($num): [lindex [lindex $news($feed:$chan) $idx] 0]"
  322. puthelp "notice $nick :description($num): [lindex [lindex $news($feed:$chan) $idx] 2]"
  323. puthelp "notice $nick :.......link($num): [lindex [lindex $news($feed:$chan) $idx] 1]"
  324. return 1
  325. }
  326. }
  327.  
  328. # this proc courtesy of RS,
  329. # from http://wiki.tcl.tk/775
  330. proc b64en str {
  331. binary scan $str B* bits
  332. switch [expr {[string length $bits]%6}] {
  333. 0 {set tail ""}
  334. 2 {append bits 0000; set tail ==}
  335. 4 {append bits 00; set tail =}
  336. }
  337. return [string map {
  338. 000000 A 000001 B 000010 C 000011 D 000100 E 000101 F
  339. 000110 G 000111 H 001000 I 001001 J 001010 K 001011 L
  340. 001100 M 001101 N 001110 O 001111 P 010000 Q 010001 R
  341. 010010 S 010011 T 010100 U 010101 V 010110 W 010111 X
  342. 011000 Y 011001 Z 011010 a 011011 b 011100 c 011101 d
  343. 011110 e 011111 f 100000 g 100001 h 100010 i 100011 j
  344. 100100 k 100101 l 100110 m 100111 n 101000 o 101001 p
  345. 101010 q 101011 r 101100 s 101101 t 101110 u 101111 v
  346. 110000 w 110001 x 110010 y 110011 z 110100 0 110101 1
  347. 110110 2 110111 3 111000 4 111001 5 111010 6 111011 7
  348. 111100 8 111101 9 111110 + 111111 /
  349. } $bits]$tail
  350. }
  351.  
  352. variable html {
  353. &quot; \x22 &apos; \x27 &amp; \x26 &lt; \x3C
  354. &gt; \x3E &nbsp; \x20 &iexcl; \xA1 &curren; \xA4
  355. &cent; \xA2 &pound; \xA3 &yen; \xA5 &brvbar; \xA6
  356. &sect; \xA7 &uml; \xA8 &copy; \xA9 &ordf; \xAA
  357. &laquo; \xAB &not; \xAC &shy; \xAD &reg; \xAE
  358. &macr; \xAF &deg; \xB0 &plusmn; \xB1 &sup2; \xB2
  359. &sup3; \xB3 &acute; \xB4 &micro; \xB5 &para; \xB6
  360. &middot; \xB7 &cedil; \xB8 &sup1; \xB9 &ordm; \xBA
  361. &raquo; \xBB &frac14; \xBC &frac12; \xBD &frac34; \xBE
  362. &iquest; \xBF &times; \xD7 &divide; \xF7 &Agrave; \xC0
  363. &Aacute; \xC1 &Acirc; \xC2 &Atilde; \xC3 &Auml; \xC4
  364. &Aring; \xC5 &AElig; \xC6 &Ccedil; \xC7 &Egrave; \xC8
  365. &Eacute; \xC9 &Ecirc; \xCA &Euml; \xCB &Igrave; \xCC
  366. &Iacute; \xCD &Icirc; \xCE &Iuml; \xCF &ETH; \xD0
  367. &Ntilde; \xD1 &Ograve; \xD2 &Oacute; \xD3 &Ocirc; \xD4
  368. &Otilde; \xD5 &Ouml; \xD6 &Oslash; \xD8 &Ugrave; \xD9
  369. &Uacute; \xDA &Ucirc; \xDB &Uuml; \xDC &Yacute; \xDD
  370. &THORN; \xDE &szlig; \xDF &agrave; \xE0 &aacute; \xE1
  371. &acirc; \xE2 &atilde; \xE3 &auml; \xE4 &aring; \xE5
  372. &aelig; \xE6 &ccedil; \xE7 &egrave; \xE8 &eacute; \xE9
  373. &ecirc; \xEA &euml; \xEB &igrave; \xEC &iacute; \xED
  374. &icirc; \xEE &iuml; \xEF &eth; \xF0 &ntilde; \xF1
  375. &ograve; \xF2 &oacute; \xF3 &ocirc; \xF4 &otilde; \xF5
  376. &ouml; \xF6 &oslash; \xF8 &ugrave; \xF9 &uacute; \xFA
  377. &ucirc; \xFB &uuml; \xFC &yacute; \xFD &thorn; \xFE
  378. &yuml; \xFF
  379. }
  380.  
  381. }
  382.  
  383.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement