Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
drss/drss.tcl
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
441 lines (420 sloc)
16.7 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ################################################################################ | |
| # Drss script is a fork of rssnews.tcl by lee8oi@gmail.com # | |
| ################################################################################ | |
| #!!!!!!!!!!!!!!!!!!!!!!! Original rssnews header !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!# | |
| #######################################################################!!!!!!!!# | |
| # #!!!!!!!!# | |
| # rssnews.tcl - RSS news announcer for eggdrop by demond@demond.net #!!!!!!!!# | |
| # #!!!!!!!!# | |
| # this will announce the updated news from RSS feed(s), #!!!!!!!!# | |
| # periodically polling the feed(s); supports multiple #!!!!!!!!# | |
| # channels/multiple feeds per channel; you only need to #!!!!!!!!# | |
| # set up your feeds array, see below; secure (SSL) and #!!!!!!!!# | |
| # private (password-protected) feeds are also supported #!!!!!!!!# | |
| # #!!!!!!!!# | |
| # Usage: !news <feed name> [news index #] - from channel #!!!!!!!!# | |
| # .rss <add|del|list> [name:#chan] - from partyline #!!!!!!!!# | |
| # #!!!!!!!!# | |
| #######################################################################!!!!!!!!# | |
| #!!!!!!!!!!!!!!!!!!!!!!!! Original rssnews header !!!!!!!!!!!!!!!!!!!!!!!!!!!!!# | |
| ################################################################################ | |
| package require Tcl 8.3 | |
| package require eggdrop 1.6 | |
| package require http 2.0 | |
| namespace eval drss { | |
| # set your feed(s) sources here: feed name, channel, poll frequency in mins, feed URL | |
| # | |
| #set feeds(osnews:#chan1) {17 http://www.osnews.com/files/recent.rdf} | |
| #set feeds(google:#chan2) {11 http://news.google.com/news?ned=us&topic=h&output=rss} | |
| #set feeds(linuxtoday:#mageia-social) {10 http://feeds.feedburner.com/linuxtoday/linux?format=xml} | |
| # if you have to use password-protected feed, set it up like this: | |
| # | |
| #set feeds(name3:#chan3) {13 http://some.site.com/feed username password} | |
| # maximum number of announced new headlines | |
| # | |
| variable maxnew 5 | |
| # feed fetch timeout in seconds | |
| # | |
| variable timeout 20 | |
| # public trigger flood settings | |
| # | |
| variable pubflud 5:15 | |
| # support SSL feeds (requires TLS package) | |
| # | |
| variable usessl 0 | |
| # if usessl is 1, request/require valid certificate from server | |
| # | |
| variable reqcert yes:no | |
| ####################################################################### | |
| # nothing to edit below | |
| variable version "drss-0.1.1" | |
| if {$usessl} { | |
| package require tls 1.5 | |
| scan $reqcert {%[^:]:%s} r1 r2 | |
| if {$r1 == "yes"} {set r1 1} {set r1 0} | |
| if {$r2 == "yes"} {set r2 1} {set r2 0} | |
| set ssl [list ::tls::socket -request $r1 -require $r2] | |
| ::http::register https 443 $ssl | |
| } | |
| bind dcc m rss [namespace current]::rss | |
| bind pub - !news [namespace current]::news | |
| bind pub - !rss [namespace current]::news | |
| bind time - * [namespace current]::timer | |
| putlog "$version by lee8oi loaded" | |
| proc timer {min hour day month year} { | |
| variable feeds | |
| if {[info exists feeds]} { | |
| set mins [expr [scan $min %d]+[scan $hour %d]*60] | |
| foreach {chanfeed settings} [array get feeds] { | |
| if {$mins % [lindex $settings 0] == 0} { | |
| if {[llength $settings] > 2} { | |
| foreach {t url user pass} $settings {break} | |
| fetch $url $chanfeed $user $pass | |
| } { | |
| foreach {t url} $settings {break} | |
| fetch $url $chanfeed | |
| } | |
| } | |
| }} | |
| } | |
| proc fetch {url chanfeed args} { | |
| variable timeout | |
| variable version; variable token | |
| set to [expr {$timeout * 1000}] | |
| set cmd [namespace current]::callback | |
| if {[llength $args] > 0} { | |
| foreach {user pass} $args {break} | |
| set hdr [list Authorization "Basic [b64en $user:$pass]"] | |
| } { set hdr {}} | |
| ::http::config -useragent "$version by demond" | |
| if {[catch {set t [::http::geturl $url -command $cmd -timeout $to -headers $hdr]} err]} { | |
| putlog "$version: ERROR($chanfeed): $err" | |
| } { | |
| set token($t) [list $url $chanfeed $args] | |
| } | |
| } | |
| proc callback {t} { | |
| variable version; variable token | |
| foreach {url chanfeed args} $token($t) {break} | |
| switch -exact [::http::status $t] { | |
| "timeout" { | |
| putlog "$version: ERROR($chanfeed): timeout" | |
| } | |
| "error" { | |
| putlog "$version: ERROR($chanfeed): [::http::error $t]" | |
| } | |
| "ok" { | |
| switch -glob [::http::ncode $t] { | |
| 3* { | |
| upvar #0 $t state | |
| array set meta $state(meta) | |
| fetch $meta(Location) $chanfeed $args | |
| } | |
| 200 { | |
| process [::http::data $t] $chanfeed | |
| } | |
| default { | |
| putlog "$version: ERROR($chanfeed): [::http::code $t]" | |
| }} | |
| } | |
| default { | |
| putlog "$version: ERROR($chanfeed): got EOF from socket" | |
| }} | |
| ::http::cleanup $t | |
| } | |
| proc process {data chanfeed} { | |
| variable news; variable hash | |
| variable maxnew; variable source | |
| set idv 1; set count 0; | |
| scan $chanfeed {%[^:]:%s} feed chan | |
| set news($chanfeed) {}; set source($chanfeed) "" | |
| set data [webbydescdecode $data] | |
| if {[regexp {(?i)<title>(.*?)</title>} $data -> foo]} { | |
| append source($chanfeed) $foo | |
| } | |
| if {[regexp {(?i)<description>(.*?)</description>} $data -> foo]} { | |
| append source($chanfeed) " | $foo" | |
| } | |
| set infoline $source($chanfeed) | |
| regsub -all {(?i)<items.*?>.*?</items>} $data {} data | |
| foreach {foo item} [regexp -all -inline {(?i)<item.*?>(.*?)</item>} $data] { | |
| regexp {(?i)<title.*?>(.*?)</title>} $item -> title | |
| regexp {(?i)<link.*?>(.*?)</link} $item -> link | |
| regexp {(?i)<desc.*?>(.*?)</desc.*?>} $item -> descr | |
| set title [unhtml $title]; set link [unhtml $link]; set descr [unhtml $descr] | |
| if {![info exists title]} {set title "(none)"} | |
| if {![info exists link]} {set link "(none)"} | |
| if {![info exists descr]} {set descr "(none)"} | |
| if {[info exists hash($chanfeed)]} { | |
| if {[lsearch -exact $hash($chanfeed) [md5 $title]] == -1 && [botonchan $chan]} { | |
| #if {![info exists header]} { | |
| # puthelp "notice $chan :\002Breaking news\002 from the $feed feed." | |
| # if {$infoline == ""} {set header $feed} {set header $infoline} | |
| # set header [unhtml $header] | |
| # #puthelp "notice $chan :\002Breaking news\002: $title ~ $link" | |
| #} | |
| if {$count < $maxnew} { | |
| set link "[webbytiny $link 5]" | |
| puthelp "notice $chan :~$feed $idv~ $title ~ $link" | |
| incr count | |
| } { | |
| lappend indices $idv | |
| } | |
| }} | |
| lappend news($chanfeed) [list $title $link $descr] | |
| lappend hashes [md5 $title] | |
| incr idv | |
| } | |
| if {[info exists indices] && [botonchan $chan]} { | |
| set count [llength $indices] | |
| set indices "(indices: [join $indices {, }])" | |
| puthelp "notice $chan :...and $count more $indices" | |
| set indices 0 | |
| } | |
| set hash($chanfeed) $hashes | |
| } | |
| proc rss {hand idx text} { | |
| variable feeds | |
| if {$text == ""} { | |
| putdcc $idx "Usage: .$::lastbind <add|del|list> \[name:#chan \[feed\]\]" | |
| return | |
| } | |
| set text [split $text] | |
| switch [lindex $text 0] { | |
| "list" { | |
| if {[info exists feeds]} { | |
| foreach {chanfeed settings} [array get feeds] { | |
| putdcc $idx "$chanfeed -> [join $settings]" | |
| }} | |
| } | |
| "add" { | |
| if {[llength $text] < 4} { | |
| putdcc $idx "not enough add arguments" | |
| return | |
| } | |
| set chanfeed [lindex $text 1] | |
| if {[info exists feeds]} { | |
| set names [string tolower [array names feeds]] | |
| if {[lsearch -exact $names [string tolower $chanfeed]] != -1} { | |
| putdcc $idx "$chanfeed already exists" | |
| return | |
| }} | |
| set feeds($chanfeed) [lrange $text 2 end] | |
| } | |
| "del" { | |
| set chanfeed [lindex $text 1] | |
| if {[info exists feeds]} { | |
| set names [string tolower [array names feeds]] | |
| if {[lsearch -exact $names [string tolower $chanfeed]] == -1} { | |
| putdcc $idx "$chanfeed does not exist" | |
| return | |
| } { | |
| unset feeds($chanfeed) | |
| }} | |
| } | |
| default { | |
| putdcc $idx "invalid sub-command" | |
| return | |
| } | |
| } | |
| return 1 | |
| } | |
| proc news {nick uhost hand chan text} { | |
| variable source | |
| variable news; variable feeds | |
| variable pcount; variable pubflud | |
| if {[info exists pcount]} { | |
| set n [lindex $pcount 1]; incr n | |
| set ts [lindex $pcount 0] | |
| set pcount [list $ts $n] | |
| scan $pubflud {%[^:]:%s} maxr maxt | |
| if {$n >= $maxr} { | |
| if {[unixtime] - $ts <= $maxt} {return} | |
| set n 1; set ts [unixtime] | |
| } | |
| } { | |
| set n 1; set ts [unixtime] | |
| } | |
| set pcount [list $ts $n] | |
| set num [lindex [split $text] 1] | |
| set feed [lindex [split $text] 0] | |
| set text [unhtml $text] | |
| if {$text == ""} { | |
| foreach {key value} [array get feeds] { | |
| scan $key {%[^:]:%s} name channel | |
| if {[string eq -noc $chan $channel]} { | |
| lappend names $name | |
| } | |
| } | |
| if {[info exists names]} { | |
| set names [join $names {, }] | |
| puthelp "notice $nick :feed(s) for $chan: $names" | |
| puthelp "notice $nick :type $::lastbind <feed> \[index#\]" | |
| } { | |
| puthelp "notice $nick :no feed(s) for $chan" | |
| } | |
| return 1 | |
| } | |
| if {![info exists news($feed:$chan)]} { | |
| puthelp "notice $nick :no news from $feed on $chan" | |
| return 1 | |
| } | |
| if {$num == ""} { | |
| set idx 1 | |
| if {$source($feed:$chan) != ""} { | |
| set title $source($feed:$chan) | |
| } { | |
| set title [lindex $feeds($feed:$chan) 1] | |
| } | |
| putlog "feeds(feed:chan) 2 is: [lindex $feeds($feed:$chan) 0]" | |
| set title [unhtml $title] | |
| #puthelp "notice $chan :News source: \002$title\002" | |
| foreach item $news($feed:$chan) { | |
| set link "[webbytiny [lindex $item 1] 5]" | |
| puthelp "notice $nick :~$feed~$idx~ [lindex $item 0] ~ $link" | |
| incr idx | |
| } | |
| return 1 | |
| } elseif {![string is integer $num]} { | |
| puthelp "notice $chan :news index must be number" | |
| return 1 | |
| } | |
| if {$num < 1 || $num > [llength $news($feed:$chan)]} { | |
| puthelp "notice $nick :no such news index, try $::lastbind $feed" | |
| return 1 | |
| } { | |
| set idx [expr {$num-1}] | |
| puthelp "notice $nick :~title~$num~ [lindex [lindex $news($feed:$chan) $idx] 0]" | |
| puthelp "notice $nick :~description~ [lindex [lindex $news($feed:$chan) $idx] 2]" | |
| puthelp "notice $nick :~link~ [lindex [lindex $news($feed:$chan) $idx] 1]" | |
| return 1 | |
| } | |
| } | |
| # this proc courtesy of RS, | |
| # from http://wiki.tcl.tk/775 | |
| proc b64en str { | |
| binary scan $str B* bits | |
| switch [expr {[string length $bits]%6}] { | |
| 0 {set tail ""} | |
| 2 {append bits 0000; set tail ==} | |
| 4 {append bits 00; set tail =} | |
| } | |
| return [string map { | |
| 000000 A 000001 B 000010 C 000011 D 000100 E 000101 F | |
| 000110 G 000111 H 001000 I 001001 J 001010 K 001011 L | |
| 001100 M 001101 N 001110 O 001111 P 010000 Q 010001 R | |
| 010010 S 010011 T 010100 U 010101 V 010110 W 010111 X | |
| 011000 Y 011001 Z 011010 a 011011 b 011100 c 011101 d | |
| 011110 e 011111 f 100000 g 100001 h 100010 i 100011 j | |
| 100100 k 100101 l 100110 m 100111 n 101000 o 101001 p | |
| 101010 q 101011 r 101100 s 101101 t 101110 u 101111 v | |
| 110000 w 110001 x 110010 y 110011 z 110100 0 110101 1 | |
| 110110 2 110111 3 111000 4 111001 5 111010 6 111011 7 | |
| 111100 8 111101 9 111110 + 111111 / | |
| } $bits]$tail | |
| } | |
| proc webbytiny {url type} { | |
| set ua "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5" | |
| set http [::http::config -useragent $ua -urlencoding "utf-8"] | |
| switch -- $type { | |
| 4 { set type [rand 4] } | |
| 5 { if {![info exists ::webbyCount]} { | |
| set ::webbyCount 0 | |
| set type 0 | |
| } else { | |
| set type [expr {[incr ::webbyCount] % 4}] | |
| } | |
| } | |
| } | |
| switch -- $type { | |
| 0 { set query "http://tinyurl.com/api-create.php?[http::formatQuery url $url]" } | |
| 1 { set query "http://is.gd/api.php?[http::formatQuery longurl $url]" } | |
| 2 { set query "http://is.gd/api.php?[http::formatQuery longurl $url]" } | |
| 3 { set query "http://is.gd/api.php?[http::formatQuery longurl $url]" } | |
| } | |
| set token [http::geturl $query -timeout 3000] | |
| upvar #0 $token state | |
| if {[string length $state(body)]} { return [string map {"\n" ""} $state(body)] } | |
| return $url | |
| } | |
| proc unhtml {text} { | |
| regsub -all "(?:<b>|</b>|<b />|<em>|</em>|<strong>|</strong>)" $text "\002" text | |
| regsub -all "(?:<u>|</u>|<u />)" $text "\037" text | |
| regsub -all "(?:<br>|<br/>|<br />)" $text ". " text | |
| regsub -all "<script.*?>.*?</script>" $text "" text | |
| regsub -all "<style.*?>.*?</style>" $text "" text | |
| regsub -all -- {<.*?>} $text " " text | |
| while {[string match "* *" $text]} { regsub -all " " $text " " text } | |
| return [string trim $text] | |
| } | |
| proc webbydescdecode {text} { | |
| # code below is neccessary to prevent numerous html markups | |
| # from appearing in the output (ie, ", ᘧ, etc) | |
| # stolen (borrowed is a better term) from perplexa's urban | |
| # dictionary script.. | |
| if {![string match *&* $text]} {return $text} | |
| set escapes { | |
| \xa0 ¡ \xa1 ¢ \xa2 £ \xa3 ¤ \xa4 | |
| ¥ \xa5 ¦ \xa6 § \xa7 ¨ \xa8 © \xa9 | |
| ª \xaa « \xab ¬ \xac ­ \xad ® \xae | |
| ¯ \xaf ° \xb0 ± \xb1 ² \xb2 ³ \xb3 | |
| ´ \xb4 µ \xb5 ¶ \xb6 · \xb7 ¸ \xb8 | |
| ¹ \xb9 º \xba » \xbb ¼ \xbc ½ \xbd | |
| ¾ \xbe ¿ \xbf À \xc0 Á \xc1 Â \xc2 | |
| Ã \xc3 Ä \xc4 Å \xc5 Æ \xc6 Ç \xc7 | |
| È \xc8 É \xc9 Ê \xca Ë \xcb Ì \xcc | |
| Í \xcd Î \xce Ï \xcf Ð \xd0 Ñ \xd1 | |
| Ò \xd2 Ó \xd3 Ô \xd4 Õ \xd5 Ö \xd6 | |
| × \xd7 Ø \xd8 Ù \xd9 Ú \xda Û \xdb | |
| Ü \xdc Ý \xdd Þ \xde ß \xdf à \xe0 | |
| á \xe1 â \xe2 ã \xe3 ä \xe4 å \xe5 | |
| æ \xe6 ç \xe7 è \xe8 é \xe9 ê \xea | |
| ë \xeb ì \xec í \xed î \xee ï \xef | |
| ð \xf0 ñ \xf1 ò \xf2 ó \xf3 ô \xf4 | |
| õ \xf5 ö \xf6 ÷ \xf7 ø \xf8 ù \xf9 | |
| ú \xfa û \xfb ü \xfc ý \xfd þ \xfe | |
| ÿ \xff ƒ \u192 Α \u391 Β \u392 Γ \u393 Δ \u394 | |
| Ε \u395 Ζ \u396 Η \u397 Θ \u398 Ι \u399 | |
| Κ \u39A Λ \u39B Μ \u39C Ν \u39D Ξ \u39E | |
| Ο \u39F Π \u3A0 Ρ \u3A1 Σ \u3A3 Τ \u3A4 | |
| Υ \u3A5 Φ \u3A6 Χ \u3A7 Ψ \u3A8 Ω \u3A9 | |
| α \u3B1 β \u3B2 γ \u3B3 δ \u3B4 ε \u3B5 | |
| ζ \u3B6 η \u3B7 θ \u3B8 ι \u3B9 κ \u3BA | |
| λ \u3BB μ \u3BC ν \u3BD ξ \u3BE ο \u3BF | |
| π \u3C0 ρ \u3C1 ς \u3C2 σ \u3C3 τ \u3C4 | |
| υ \u3C5 φ \u3C6 χ \u3C7 ψ \u3C8 ω \u3C9 | |
| ϑ \u3D1 ϒ \u3D2 ϖ \u3D6 • \u2022 | |
| … \u2026 ′ \u2032 ″ \u2033 ‾ \u203E | |
| ⁄ \u2044 ℘ \u2118 ℑ \u2111 ℜ \u211C | |
| ™ \u2122 ℵ \u2135 ← \u2190 ↑ \u2191 | |
| → \u2192 ↓ \u2193 ↔ \u2194 ↵ \u21B5 | |
| ⇐ \u21D0 ⇑ \u21D1 ⇒ \u21D2 ⇓ \u21D3 ⇔ \u21D4 | |
| ∀ \u2200 ∂ \u2202 ∃ \u2203 ∅ \u2205 | |
| ∇ \u2207 ∈ \u2208 ∉ \u2209 ∋ \u220B ∏ \u220F | |
| ∑ \u2211 − \u2212 ∗ \u2217 √ \u221A | |
| ∝ \u221D ∞ \u221E ∠ \u2220 ∧ \u2227 ∨ \u2228 | |
| ∩ \u2229 ∪ \u222A ∫ \u222B ∴ \u2234 ∼ \u223C | |
| ≅ \u2245 ≈ \u2248 ≠ \u2260 ≡ \u2261 ≤ \u2264 | |
| ≥ \u2265 ⊂ \u2282 ⊃ \u2283 ⊄ \u2284 ⊆ \u2286 | |
| ⊇ \u2287 ⊕ \u2295 ⊗ \u2297 ⊥ \u22A5 | |
| ⋅ \u22C5 ⌈ \u2308 ⌉ \u2309 ⌊ \u230A | |
| ⌋ \u230B ⟨ \u2329 ⟩ \u232A ◊ \u25CA | |
| ♠ \u2660 ♣ \u2663 ♥ \u2665 ♦ \u2666 | |
| " \x22 & \x26 < \x3C > \x3E O&Elig; \u152 œ \u153 | |
| Š \u160 š \u161 Ÿ \u178 ˆ \u2C6 | |
| ˜ \u2DC   \u2002   \u2003   \u2009 | |
| ‌ \u200C ‍ \u200D ‎ \u200E ‏ \u200F – \u2013 | |
| — \u2014 ‘ \u2018 ’ \u2019 ‚ \u201A | |
| “ \u201C ” \u201D „ \u201E † \u2020 | |
| ‡ \u2021 ‰ \u2030 ‹ \u2039 › \u203A | |
| € \u20AC ' \u0027 ‎ "" ‏ "" ‬ "" ‭ "" | |
| ‮ "" — \u2014 | |
| }; | |
| set text [string map [list "\]" "\\\]" "\[" "\\\[" "\$" "\\\$" "\\" "\\\\"] [string map $escapes $text]] | |
| regsub -all -- {&#([[:digit:]]{1,5});} $text {[format %c [string trimleft "\1" "0"]]} text | |
| regsub -all -- {&#x([[:xdigit:]]{1,4});} $text {[format %c [scan "\1" %x]]} text | |
| regsub -all -- {\\x([[:xdigit:]]{1,2})} $text {[format %c [scan "\1" %x]]} text | |
| set text [subst "$text"] | |
| return $text | |
| } | |
| } |