AndrzejL

rssnews.tcl

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