1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
|
set pkgs {"json" "json::write" "http" "tls"}
foreach pkg $pkgs {
if {[catch {package require $pkg}]} {
putlog "$pkg is not installed. Autoscripts cannot load"
putlog "$pkg can be installed via your host's command line package manager"
return 1
}
}
set asidx 0
set eggdir "autoscripts"
set cmdtxt "\nEnter your command ('done' to return to partyline):"
set jsondict [dict create]
set asmajor 1
set asminor 0
bind DCC n autoscript asconsole
proc asconsole {hand idx arg} {
global echostatus
global oldchan
global asidx
set oldchan [getchan $idx]
set echostatus [echo $idx]
set asidx $idx
setchan $idx 469
echo $idx 0
bind FILT n * parse_egg
bind evnt n prerehash {egg_done $asidx}
putdcc $idx " _ _ _ _ "
putdcc $idx " /_\\ _ _| |_ ___ ___ ___ _ __(_)_ __ | |_ "
putdcc $idx " //_\\\\| | | | __/ _ \\/ __|/ __| '__| | '_ \\| __|"
putdcc $idx "/ _ \\ |_| | || (_) \\__ \\ (__| | | | |_) | |_ "
putdcc $idx "\\_/ \\_/\\__,_|\\__\\___/|___/\\___|_| |_| .__/ \\__|"
putdcc $idx " |_| "
putdcc $idx "=======================================================\n"
putdcc $idx "Welcome to the autoscript console. Enter your command: "
}
#Read all JSON script files
proc readjsonfile {} {
global jsondict
global eggdir
set jsonlist {}
### How to get filepath properly
foreach dirname [glob -nocomplain -type d -directory $eggdir *] {
if {![file exists $dirname/manifest.json]} {
file delete -force $dirname
putlog "$dirname missing manifest.json, deleting"
} else {
set fs [open $dirname/manifest.json r]
set contents [read $fs]
set jcontents [json::json2dict $contents]
if {[dict exists $jcontents schema]} {
if {![string equal [dict get $jcontents schema] "1"]} {
putlog "$dirname contains invalid manifest.json format, skipping..."
continue
}
}
lappend jsonlist $contents
close $fs
}
}
set jsondict [json::json2dict "\[[join $jsonlist {,}]\]"]
}
# Write a script's JSON content to file
proc write_json {script jstring} {
global eggdir
set fs [open $eggdir/${script}/manifest.json w]
puts $fs $jstring
close $fs
}
# Send an HTTP request. Type 0 for text, 1 for binary
proc send_http {url type} {
global eggdir
http::register https 443 [list ::tls::socket -autoservername true]
set req [http::config -useragent "eggdrop"]
if {$type} {
set fs [open $eggdir/[lindex [split $url /] end] w]
catch {set req [http::geturl $url -binary 1 -channel $fs]} error
close $fs
} else {
catch {set req [http::geturl $url -headers [dict create "User-Agent" "Mozilla/5.0"] -timeout 10000]} error
}
set status [http::status $req]
if {$status != "ok"} {
putlog "HTTP request error: $error"
return
}
set data [http::data $req]
::http::cleanup $req
return $data
}
proc createvarns {varname} {
for {set i 0} {$i < [llength [split $varname ::]] - 1} {incr i} {
namespace eval [join [lrange [split $varname ::] 0 $i] ::] {}
}
}
# Read all manifest files
proc loadscripts {} {
global jsondict
global eggdir
foreach scriptentry $jsondict {
if [dict get $scriptentry config loaded] {
if {[dict exists $scriptentry config vars]} {
foreach configvar [dict keys [dict get $scriptentry config vars] *] {
uplevel #0 [list createvarns $configvar]
set ::$configvar [dict get $scriptentry config vars $configvar value]
}
}
if {[catch {uplevel #0 [list source $eggdir/[dict get $scriptentry name]/[dict get $scriptentry name].tcl]} err]} {
putlog "Error loading [dict get $scriptentry name]: $err"
return
}
}
}
}
# Initial function called from autoscript console, sends to proper proc based on args
proc parse_egg {idx text} {
global echostatus
global oldchan
global asidx
# Check if this is the user who triggered the console
if {$idx != $asidx} {
return $text
}
set args [split $text]
set args [lassign $args subcmd arg1 arg2]
if {$subcmd in {done}} {
egg_done $idx "parse"
} elseif {$subcmd in {remote list help}} {
egg_$subcmd $idx
} elseif {$subcmd in {update}} {
egg_$subcmd $idx $arg1
} elseif {$subcmd in {config fetch clean}} {
if {$arg1 eq ""} {
putdcc $idx "Missing parameter, must be $::lastbind $subcmd scriptName"
} else {
egg_$subcmd $idx $arg1
}
} elseif {$subcmd in {load unload}} {
if {$arg1 eq ""} {
putdcc $idx "Missing parameter, must be $::lastbind $subcmd scriptName"
} else {
egg_load $idx $arg1 [expr {$subcmd eq "load"}]
}
} elseif {$subcmd in {set}} {
if {![llength $args]} {
putdcc $idx "Missing parameter, must be $::lastbind $subcmd scriptName settingName newSettingValue"
} else {
egg_$subcmd $idx $arg1 $arg2 [join $args]
}
} elseif {$subcmd in {loaded unloaded all help}} {
egg_$subcmd
} else {
putdcc $idx "Missing or unknown subcommand"
egg_help $idx
}
return
}
# List scripts that are locally present in .egg
proc egg_list {idx} {
global jsondict
global cmdtxt
readjsonfile
putdcc $idx "\nThe following scripts are available for configuration:"
putdcc $idx "-------------------------------------------------------"
if {[llength $jsondict] == 0} {
putdcc $idx "* No scripts have been downloaded"
}
foreach script $jsondict {
set loaded [expr {[dict get $script config loaded] == 1 ? "\[X\]" : "\[ \]"}]
putdcc $idx "* $loaded [dict get $script name] (v[dict get $script version_major].[dict get $script version_minor]) - [dict get $script description]"
if {[dict exists $script config requires] && [string length [dict get $script config requires]]} {
foreach pkg [dict get $script config requires] {
if {![string equal $pkg "null"]} {
if {[lsearch -exact [package names] $pkg] == -1} {
putdcc $idx " ( ^ Must install Tcl $pkg package on host before loading)"
}
}
}
}
}
putidx $idx "Additional scripts available for download can be viewed with the 'remote' command"
putidx $idx "$cmdtxt"
}
# Load or unload a script and update JSON field
# loadme is 0 to unload a script, 1 to load a script
proc egg_load {idx script loadme} {
global jsondict
global eggdir
set found 0
readjsonfile
foreach scriptentry $jsondict {
if [string match $script [dict get $scriptentry name]] {
set found 1
if {$loadme} {
if {[dict exists $scriptentry config vars]} {
foreach configvar [dict keys [dict get $scriptentry config vars] *] {
uplevel #0 [list createvarns $configvar]
set ::$configvar [dict get $scriptentry config vars $configvar value]
}
}
if {[catch {uplevel #0 [list source $eggdir/${script}/${script}.tcl]} err]} {
putdcc $idx "Error loading ${script}: $err"
return
}
dict set scriptentry config loaded 1
putdcc $idx "* Loaded $script."
} else {
dict set scriptentry config loaded 0
putdcc $idx "* Removed $script from being loaded. Restart Eggdrop to complete."
set found 1
}
write_json $script [compile_json {dict config {dict vars {dict * dict}}} $scriptentry]
readjsonfile
break
}
}
if {!$found} {
putdcc $idx "* $script not found."
}
return $found
}
# List variables available for a script
proc egg_config {idx script} {
global cmdtxt
global jsondict
set found 0
foreach scriptentry $jsondict {
if {[string match $script [dict get $scriptentry name]]} {
set found 1
if {[dict exists $scriptentry long_description]} {
putdcc $idx "\n* [dict get $scriptentry long_description]\n\n"
}
if {![dict exists $scriptentry config vars]} {
putdcc $idx "* No variables are available to configure for $script"
} else {
putdcc $idx "* The following config options are available for $script:"
putdcc $idx "---------------------------------------------------------"
putdcc $idx "Variables available for configuration via the set command: "
foreach configvar [dict keys [dict get $scriptentry config vars] *] {
set printstr "* $configvar - [dict get $scriptentry config vars $configvar description]"
if {([string length $printstr] > 110) || ([string first "\n" $printstr] ne -1)} {
if {[string first "\n" [string range $printstr 0 110]] ne -1} {
set index [string first "\n" [string range $printstr 0 110]]
} else {
set index [string last { } $printstr 109]
}
putdcc $idx "[string range $printstr 0 $index]"
set printstr [string range $printstr $index+1 end]
while {[string length $printstr] > 0} {
if {([string length $printstr] > 102) || ([string first "\n" $printstr] ne -1)} {
if {[string first "\n" [string range $printstr 0 102]] ne -1} {
set index [string first "\n" [string range $printstr 0 102]]
} else {
set printstr [string trimleft $printstr]
set index [string last { } $printstr 101]
}
putdcc $idx " [string range $printstr 0 $index]"
set printstr [string range $printstr $index+1 end]
} else {
putdcc $idx " [string trimleft $printstr]"
putdcc $idx " (current value: [dict get $scriptentry config vars $configvar value])"
putdcc $idx "\n"
break
}
}
} else {
putdcc $idx "$printstr"
putdcc $idx " (current value: [dict get $scriptentry config vars $configvar value])"
putdcc $idx "\n"
}
}
# treats udef
putdcc $idx "\nChannel settings available for configuration via .chanset: "
if {[dict exists $scriptentry config udef]} {
foreach udef [dict keys [dict get $scriptentry config udef]] {
set utype [dict get $scriptentry config udef $udef type]
set uval null
if {[dict exists $scriptentry config udef $udef value]} {
set uval [dict get $scriptentry config udef $udef value]
}
switch -nocase -- $utype {
"flag" { putdcc $idx "* $udef ($utype) : [dict get $scriptentry config udef $udef description] .chanset <channel> +$udef" }
"str" -
"int" { putdcc $idx "* $udef ($utype) : [dict get $scriptentry config udef $udef description] .chanset <channel> $udef $uval" }
default { putdcc $idx "* $udef seems to exist but is not well defined" }
}
}
putdcc $idx ""
}
}
}
}
if {!$found} {
putdcc $idx "Script $script not found."
}
putidx $idx "$cmdtxt"
}
# Set a variable for a script
proc egg_set {idx script setting value} {
global cmdtxt
global jsondict
set noscript 1
set noset 1
foreach scriptentry $jsondict {
if {[string match $script [dict get $scriptentry name]]} {
set noscript 0
if [dict exists $scriptentry config vars $setting] {
set noset 0
dict set scriptentry config vars $setting value $value
write_json $script [compile_json {dict config {dict vars {dict * dict}}} $scriptentry]
putdcc $idx "* ${script}: Variable \"$setting\" set to \"${value}\""
putdcc $idx "* Use \"load $script\" to enable this change"
readjsonfile
}
}
}
if $noscript {
putdcc $idx "ERROR: Script \"${script}\" not found."
} elseif $noset {
putdcc $idx "* ${script}: Setting \"$setting\" not found."
}
putidx $idx "$cmdtxt"
}
# Pull down remote Tcl file listing
# For future eggheads- 100 is the maximum WP supports without doing pagination
proc egg_remote {idx} {
global cmdtxt
global eggdir
putdcc $idx "* Retrieving script list, please wait..."
send_http "https://raw.githubusercontent.com/eggheads/autoscripts/master/manifest" 1
set fp [open $eggdir/manifest r]
set jsondata [read $fp]
close $fp
set datadict [json::json2dict $jsondata]
putdcc $idx "Scripts available for download:"
putdcc $idx "-------------------------------"
foreach scriptentry $datadict {
# regsub -all {<[^>]+>} [dict get $scriptentry caption rendered] "" scriptcap
if {![string equal -nocase [dict get $scriptentry name] "autoscripts"]} {
putdcc $idx "* [format "%-16s %s" [dict get $scriptentry name] [dict get $scriptentry description]]"
}
}
putdcc $idx "\n"
putdcc $idx "* Type 'fetch <scriptname>' to download a script"
putidx $idx "$cmdtxt"
}
# Helper command for scripts- return a Tcl list of scripts that are loaded
proc egg_loaded {} {
global jsondict
list scriptlist
foreach scriptentry $jsondict {
if {[dict get $scriptentry config loaded]} {
lappend scriptlist [dict get $scriptentry name]
}
}
return $scriptlist
}
proc egg_update {idx tgtscript} {
global cmdtxt
global jsondict
global version
global asminor
global asmajor
set found 0
readjsonfile
set jsondata [send_http "https://raw.githubusercontent.com/eggheads/autoscripts/master/manifest" 0]
set datadict [json::json2dict $jsondata]
foreach localscript $jsondict {
foreach remotescript $datadict {
if {[string equal -nocase [dict get $remotescript name] [dict get $localscript name]]} {
if { ([dict get $remotescript version_minor] > [dict get $localscript version_minor] &&
[dict get $remotescript version_major] >= [dict get $localscript version_major]) ||
([dict get $remotescript version_major] > [dict get $localscript version_major]) } {
## If we're looking for a specific script, suppress other found messages
if {[string equal -nocase $tgtscript ""]} {
putdcc $idx "* [dict get $localscript name] has an update available."
}
set found 1
if {[string equal -nocase $tgtscript [dict get $localscript name]]} {
putdcc $idx "* Script update feature goes here- coming soon!"
}
}
}
}
}
if {!$found} {
putdcc $idx "* No updates available."
}
}
foreach script $jsondict {
set loaded [expr {[dict get $script config loaded] == 1 ? "\[X\]" : "\[ \]"}]
putdcc $idx "* $loaded [dict get $script name] (v[dict get $script version_major].[dict get $script version_minor]) - [dict get $script description]"
set asversion [send_http "https://www.eggheads.org/wp-content/uploads/simple-file-list/autoscript.txt" 0]
lassign [split $asversion .] major minor
if { ($minor > $asminor && $major >= $asmajor) || ($major > $asmajor)} {
putdcc $idx "* New version of autoscript found!"
} else {
putdcc $idx "* autoscript is up to date."
}
}
# Helper command for scripts- return a Tcl list of scripts that are loaded
proc egg_unloaded {} {
global jsondict
list scriptlist
foreach scriptentry $jsondict {
if {![dict get $scriptentry config loaded]} {
lappend scriptlist [dict get $scriptentry name]
}
}
return $scriptlist
}
# Helper command for scripts- return a Tcl list of scripts that are loaded
proc egg_all {} {
global jsondict
list scriptlist
foreach scriptentry $jsondict {
lappend scriptlist [dict get $scriptentry name]
}
return $scriptlist
}
# Download a script from the eggheads repository
proc egg_fetch {idx script} {
global cmdtxt
global eggdir
try {
set results [exec which tar]
set status 0
} trap CHILDSTATUS {results options} {
if {lindex [dict get $options -errorcode] 2} {
putdcc $idx "* ERROR: This feature is not available (tar not detected on this system, cannot extract a downloaded file)."
putidx $idx "$cmdtxt"
return
}
}
if {[file isdirectory $eggdir/$script]} {
putdcc $idx "* Script directory already exists. Not downloading again!"
putdcc $idx "* Use \"update $script\" if you're trying to update the script"
putidx $idx "$cmdtxt"
return
}
putdcc $idx "* Downloading, please wait..."
set jsondata [send_http "https://api.github.com/repos/eggheads/autoscripts/contents/packages" 0]
set datadict [json::json2dict $jsondata]
foreach scriptentry $datadict {
if {[string match ${script}.tgz [dict get $scriptentry name]]} {
send_http "[dict get $scriptentry download_url]" 1
putdcc $idx "* [dict get $scriptentry name] downloaded."
exec tar -zxf $eggdir/[dict get $scriptentry name] -C $eggdir
if {[file exists $eggdir/$script]} {
file delete $eggdir/[dict get $scriptentry name]
putdcc $idx "* [dict get $scriptentry name] extracted."
putdcc $idx "* Use 'config $script' to configure and then 'load $script' to load."
putidx $idx "$cmdtxt"
readjsonfile
} else {
putdcc $idx "* ERROR: [dict get $scriptentry name] not found. Cannot continue."
}
}
}
}
proc egg_clean {idx script} {
global cmdtxt
global eggdir
if {![egg_load $idx $script 0]} {
putdcc $idx "* Cannot remove $script"
return
}
if {[file isdirectory $eggdir/$script]} {
file delete -force $eggdir/$script
putdcc $idx "* $script deleted"
} else {
putdcc $idx "* $script not found"
}
putidx $idx "$cmdtxt"
}
proc egg_done {idx arg} {
global oldchan
global echostatus
putdcc $idx "Returning to partyline..."
unbind FILT n * parse_egg
unbind EVNT n prerehash {egg_done $asidx}
echo $idx $echostatus
setchan $idx $oldchan
}
proc egg_help {idx} {
global cmdtxt
putidx $idx "* The following commands are available for use with $::lastbind:"
putidx $idx " remote, fetch, list, config, set, load, unload, clean, done"
putidx $idx ""
putidx $idx "* remote : List scripts available for download"
putidx $idx "* fetch <script> : Download the script"
putidx $idx "* list : List all scripts present on the local system available for use"
putidx $idx "* config <script> : View configuration options for a script"
putidx $idx "* set <script> <setting> : Set the value for a script setting"
putidx $idx "* load <script> : Load a script, and enable a script to be loaded when Eggdrop starts"
putidx $idx "* unload <script> : Prevent a script from running when Eggdrop starts"
putidx $idx " (You must restart Eggdrop to stop a currently running script!)"
putidx $idx "* clean <script> : Permanently remove a script and any associated settings or files"
putidx $idx "* update \[script\] : Check for updates for autoscript, or specify a script to update"
putidx $idx "* done : Return to Eggdrop partyline"
putidx $idx "----------------------------------------------------------------------------------------------"
putidx $idx "$cmdtxt"
}
# Yikes.
proc compile_json {spec data} {
while [llength $spec] {
set type [lindex $spec 0]
set spec [lrange $spec 1 end]
switch -- $type {
dict {
lappend spec * unknown
set json {}
foreach {key val} $data {
foreach {keymatch valtype} $spec {
if {[string match $keymatch $key]} {
if {$key in {name displayname}} { set valtype string }
lappend json [subst {"$key":[compile_json $valtype $val]}]
break
}
}
}
return "{[join $json ,]}"
}
list {
if {![llength $spec]} {
set spec unknown
} else {
set spec [lindex $spec 0]
}
set json {}
foreach {val} $data {
lappend json [compile_json $spec $val]
}
return "\[[join $json ,]\]"
}
number {
if {$data eq "" || $data eq "null"} {
return null
} else {
return $data
}
}
string {
if {$data eq "" || $data eq "null"} {
return null
} else {
set new ""
for {set i 0} {$i < [string length $data]} {incr i} {
set c [string index $data $i]
set cc [scan $c %c]
if {$cc < 0x7e && $c ne "\\" && $cc >= 0x20 && $c ne "\""} {
append new $c
} else {
append new "\\u[format %04x $cc]"
}
}
return "\"$new\""
}
}
unknown {
if {$data eq "" || $data eq "null"} {
return null
} elseif {[string is double -strict $data] && ![string equal -nocase $data infinity] && ![string equal -nocase $data nan] && ![string equal -nocase $data -infinity]} {
return $data
} else {
set new ""
for {set i 0} {$i < [string length $data]} {incr i} {
set c [string index $data $i]
set cc [scan $c %c]
if {$cc < 0x7e && $c ne "\\" && $cc >= 0x20 && $c ne "\""} {
append new $c
} else {
append new "\\u[format %04x $cc]"
}
}
return "\"$new\""
}
}
}
}
}
if {![file exists autoscripts]} {file mkdir autoscripts}
readjsonfile
loadscripts
putlog "Loading autoscripts.tcl"
|