That's odd, very hot there???|| Temperature:: 293.9?C ||
That's odd, very hot there???|| Temperature:: 293.9?C ||
Code: Select all
#################################
# CTweather
###
# Version: v0.0.3
# Author : ComputerTech
# GitHub : https://GitHub.com/computertech312
###
namespace eval CTweather {
## Triggers.
# Current weather.
variable trig "!w"
##
## Flags.
# n = Owner.
# m = master
# o = Op.
# h = Halfop.
# v = Voice.
# f = Friend.
# - = Nothing.
variable flag "-|-"
##
## OpenWeatherMap API Key.
variable api "8c2a600d5d63d7fb13432fd58dcc419b"
## Metric type.
# 0 = Metric
# 1 = Imperial
variable type "0"
###
package require json
package require tls
package require http
bind PUB $flag $trig [namespace current]::current:weather
proc current:weather {nick host hand chan text} {
variable api ; variable type
if {![llength [split $text]]} {
putserv "privmsg $chan :Syntax: !w \[zip code | Location \]"
return 0
}
switch -exact $type {
"1" {variable tempy "C" ; variable windy "KPH" ; variable typex "metric"}
"0" {variable tempy "F" ; variable windy "MPH" ; variable typex "imperial"}
}
variable url "https://nominatim.openstreetmap.org/search/"
variable params [::http::formatQuery q $text format jsonv2 ]
variable jsonx [getdata $url $params]
variable name [dict get [lindex $jsonx 0] "display_name"]
variable lati [dict get [lindex $jsonx 0] "lat"]
variable long [dict get [lindex $jsonx 0] "lon"]
variable url "https://api.openweathermap.org/data/2.5/weather"
variable params [::http::formatQuery lat $lati lon $long appid $api units $typex]
variable jsonx [getdata $url $params]
variable temp [dict get [dict get $jsonx "main"] "temp"]
variable speed [dict get [dict get $jsonx "wind"] "speed"]
variable degre [dict get [dict get $jsonx "wind"] "deg"]
variable humid [dict get [dict get $jsonx "main"] "humidity"]
variable cover [dict get [dict get $jsonx "clouds"] "all"]
variable desc [dict get [lindex [dict get $jsonx weather] 0] "description"]
putserv "PRIVMSG $chan :\[\00309Weather\003\] Location: $name || Longitutde: $long || Latitude: $lati || Temperature:: ${temp}$tempy"
putserv "PRIVMSG $chan :Humidity: ${humid}% Wind:: ${speed}$windy Degree: $degre || Description:: $desc || Clouds: ${cover}%"
}
proc getdata {url params} {
::http::register https 443 [list ::tls::socket]
::http::config -useragent "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0"
putlog "$url?$params"
variable data [http::data [http::geturl "$url?$params" -timeout 10000]]
::http::cleanup $data
variable jsonx [json::json2dict $data]
return $jsonx
}
}
Code: Select all
<ComputerTech> !w london uk
<CX> [Weather] Location: London, Greater London, England, United Kingdom || Longitutde: -0.1276474 || Latitude: 51.5073219 || Temperature:: 51.94F
<CX> Humidity: 93% Wind:: 3.44MPH Degree: 110 || Description:: haze || Clouds: 25%
Code: Select all
#################################
# CTweather
###
# Version: v0.0.3 (+variable-adjusted)
# Author : ComputerTech
# GitHub : https://GitHub.com/computertech312
###
##################################
### +variable-adjusted Notes ###
# Corrected the usage of [variable] inside procs.
# Corrected the "Metric type" setting explanation.
##################################
namespace eval CTweather {
## Triggers.
# Current weather.
variable trig "!w"
##
## Flags.
# n = Owner.
# m = master
# o = Op.
# h = Halfop.
# v = Voice.
# f = Friend.
# - = Nothing.
variable flag "-|-"
##
## OpenWeatherMap API Key.
variable api "8c2a600d5d63d7fb13432fd58dcc419b"
## Metric type.
# 0 = Imperial
# 1 = Metric
variable type "0"
###
package require json
package require tls
package require http
bind PUB $flag $trig [namespace current]::current:weather
proc current:weather {nick host hand chan text} {
variable api ; variable type
if {![llength [split $text]]} {
putserv "privmsg $chan :Syntax: !w \[zip code | Location \]"
return 0
}
switch -exact $type {
"0" {set tempy "F" ; set windy "MPH" ; set typex "imperial"}
"1" {set tempy "C" ; set windy "KPH" ; set typex "metric"}
}
set url "https://nominatim.openstreetmap.org/search/"
set params [::http::formatQuery q $text format jsonv2 ]
set jsonx [getdata $url $params]
set name [dict get [lindex $jsonx 0] "display_name"]
set lati [dict get [lindex $jsonx 0] "lat"]
set long [dict get [lindex $jsonx 0] "lon"]
set url "https://api.openweathermap.org/data/2.5/weather"
set params [::http::formatQuery lat $lati lon $long appid $api units $typex]
set jsonx [getdata $url $params]
set temp [dict get [dict get $jsonx "main"] "temp"]
set speed [dict get [dict get $jsonx "wind"] "speed"]
set degre [dict get [dict get $jsonx "wind"] "deg"]
set humid [dict get [dict get $jsonx "main"] "humidity"]
set cover [dict get [dict get $jsonx "clouds"] "all"]
set desc [dict get [lindex [dict get $jsonx weather] 0] "description"]
putserv "PRIVMSG $chan :\[\00309Weather\003\] Location: $name || Longitutde: $long || Latitude: $lati || Temperature:: ${temp}$tempy"
putserv "PRIVMSG $chan :Humidity: ${humid}% || Wind:: ${speed}$windy Degree: $degre || Description:: $desc || Clouds: ${cover}%"
}
proc getdata {url params} {
::http::register https 443 [list ::tls::socket]
::http::config -useragent "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0"
putlog "$url?$params"
set data [http::data [http::geturl "$url?$params" -timeout 10000]]
::http::cleanup $data
set jsonx [json::json2dict $data]
return $jsonx
}
}
UTF-8 works for me for other tcls like youtube so that cant be it(+Simo) : !w ryadh
(@Hawk) : [Weather] Location: الرياض, مديرية تريم, Ù…ØØ§Ùظة ØØ¶Ø±Ù…وت, اليمن || Longitutde: 49.224962 || Latitude: 16.1146368 || Temperature:: 13.54C
(@Hawk) : Humidity: 44% Wind:: 1.26KPH Degree: 26 || Description:: clear sky || Clouds: 1%
ComputerTech wrote: ...
- Option for users to choose either Metric or Imperial units, according to their preferences.
...
I am open to additional feature requests, but for now, these are the key upgrades that I am focused on implementing.
Code: Select all
[23:09:53] <ATAS> [20:09:53] https://nominatim.openstreetmap.org/search/?q=%D1%81%D0%BE%D1%84%D0%B8%D1%8F%20&format=jsonv2
[23:09:53] <ATAS> [20:09:54] Tcl error [::CTweather::current:weather]: missing value to go with keyJust try the url in a browser and you'll have a self-explanatory message:FmX wrote:Hi. Script stop working.
Error i see is this:Code: Select all
[23:09:53] <ATAS> [20:09:53] https://nominatim.openstreetmap.org/search/?q=%D1%81%D0%BE%D1%84%D0%B8%D1%8F%20&format=jsonv2 [23:09:53] <ATAS> [20:09:54] Tcl error [::CTweather::current:weather]: missing value to go with key
File not found: API no longer accessible via this URL
Using the URL /search/ and /reverse/ (with slashes) is no longer supported. Please use URLs as given in the documentation.
Examples how to change the URL:
You use: https://nominatim.openstreetmap.org/search/?q=Berlin
Change to: https://nominatim.openstreetmap.org/search?q=Berlin
You use: https://nominatim.openstreetmap.org/sea ... xas/Huston
Change to: https://nominatim.openstreetmap.org/search?q=Huston, Texas, US
See github issue #3134 for more details.
Code: Select all
#################################
# CTweather
###
# Version: v0.0.4
# Author : ComputerTech
# GitHub : https://GitHub.com/computertech312
#
# DESCRIPTION:
# Provides current weather and 4-day forecasts for any city, region, or US
# zip code. Users can register their location once so they never have to
# type it again. Registered locations are geocoded on save (Nominatim) and
# stored as lat/lon so live !w/!f lookups skip the geocoding step entirely.
#
# REQUIREMENTS:
# - Eggdrop 1.8+ with Tcl 8.5+
# - Tcl TLS extension (libtls / tcl-tls) for HTTPS support
# - Tcllib (tcllib package) for the json package
#
# SETUP:
# 1. Edit the "Configuration" section below:
# weather_api_key - Your Pirate Weather API key
# Get one free at: https://pirateweather.net
# weather_data_file - Path (relative to eggdrop's working dir) where
# registered nick -> location data is persisted
#
# 2. Copy this file into your eggdrop scripts directory, e.g.:
# cp ct-weather.tcl /path/to/eggdrop/scripts/
#
# 3. Add the following line to your eggdrop config (eggdrop.conf):
# source scripts/ct-weather.tcl
#
# 4. Rehash or restart your bot:
# .rehash
#
# COMMANDS (all triggered with ! prefix in any public channel):
#
# !w [location]
# Show current weather for <location> (city name or US zip code).
# If no location is given, uses your registered location.
# Example: !w New York
# !w 90210
# !w (uses registered location)
#
# !f [location]
# Show a 4-day forecast for <location>.
# Falls back to registered location if none is provided.
# Example: !f London, UK
#
# !register_location <location>
# Geocode <location> and save it as your default. The resolved name
# and coordinates are confirmed back to you in chat.
# Example: !register_location Austin, TX
#
# !change_location <location>
# Update your registered location. You must already have one saved.
# Example: !change_location 10001
#
# !unregister_location
# Remove your registered location.
#
# !change_trigger <w|f> <newtrigger>
# (Bot owners only) Change the trigger for !w or !f at runtime.
# Changes take effect immediately without a rehash.
# Example: !change_trigger w !weather
# !change_trigger f !forecast
#
# DATA FILE FORMAT (ct-weather.dat):
# One Tcl list per line: nick lat lon {Display Name}
# Example:
# colby 30.2672 -97.7431 {Austin, United States}
#
# NOTES:
# - Temperature is always shown as C and F, e.g. 19.8°C (67.7°F).
# - Temperature is IRC-colour coded: blue (<10C), yellow (<20C),
# orange (<30C), red (30C+).
# - Geocoding uses the free Nominatim API (openstreetmap.org).
# Please respect their usage policy (max 1 req/sec, valid User-Agent).
#
# CONFIGURATION VARIABLES:
# weather_api_key - Pirate Weather API key
# weather_data_file - Path to nick->location storage file
# weather_flag - Eggdrop flag required to use commands
# "-|-" = everyone | "o|o" = ops only etc.
#
###############################################################################
namespace eval CTweather {
## Triggers
variable trig_w "!w"
variable trig_f "!f"
# --- Configuration ---
variable weather_api_key "YOUR-API-KEY-HERE"
variable weather_data_file "scripts/ct-weather.dat"
# Eggdrop channel flag required to use bot commands ("-|-" = everyone)
variable weather_flag "-|-"
# In-memory nick -> location storage
variable weather_locations
array set weather_locations {}
package require http
package require tls
package require json
# Register HTTPS handler (catch guards against double-registration on rehash)
catch {::tls::init -ssl2 0 -ssl3 0 -tls1 0 -tls1.2 1}
catch {http::register https 443 [list ::tls::socket]}
# --- Binds ---
bind pub $weather_flag !register_location [namespace current]::pub:weather_register
bind pub $weather_flag !change_location [namespace current]::pub:weather_change
bind pub $weather_flag !unregister_location [namespace current]::pub:weather_unregister
bind pub $weather_flag $trig_w [namespace current]::pub:weather_current
bind pub $weather_flag $trig_f [namespace current]::pub:weather_forecast
# !change_trigger is open to all so the proc can give a proper error to non-owners
bind pub -|- !change_trigger [namespace current]::pub:weather_change_trigger
# --- File I/O ---
proc weather_load {} {
variable weather_locations
variable weather_data_file
catch {
set fp [open $weather_data_file r]
while {[gets $fp line] >= 0} {
# Each line is a Tcl list: nick lat lon display_name
if {[llength $line] == 4} {
set nick [lindex $line 0]
set weather_locations($nick) [lrange $line 1 3]
}
}
close $fp
}
}
proc weather_save {} {
variable weather_locations
variable weather_data_file
if {[catch {
set fp [open $weather_data_file w]
foreach nick [array names weather_locations] {
lassign $weather_locations($nick) lat lon name
puts $fp [list $nick $lat $lon $name]
}
close $fp
} err]} {
putlog "ct-weather.tcl: Failed to save locations: $err"
}
}
# --- Helpers ---
proc weather_sanitize {text} {
regsub -all {[^\x20-\x7E]} $text {} text
regsub -all {\s+} [string trim $text] { } text
return $text
}
proc weather_geocode {query} {
if {[regexp {^\d{5}$} $query]} {
set query "${query}, US"
}
set url "https://nominatim.openstreetmap.org/search?[http::formatQuery q $query format json limit 1 addressdetails 1]"
set tok ""
if {[catch {
set tok [http::geturl $url \
-headers {User-Agent Eggdrop-PirateWeatherBot/1.0} \
-timeout 10000]
set body [http::data $tok]
http::cleanup $tok
set tok ""
} err]} {
if {$tok ne ""} { catch {http::cleanup $tok} }
putlog "ct-weather geocode HTTP error: $err"
return {}
}
if {[catch {set results [json::json2dict $body]} err]} {
putlog "ct-weather geocode JSON error: $err"
return {}
}
if {![llength $results]} { return {} }
set r [lindex $results 0]
set lat [dict get $r lat]
set lon [dict get $r lon]
set full [dict get $r display_name]
set parts [split $full ","]
if {[llength $parts] > 2} {
set name "[string trim [lindex $parts 0]], [string trim [lindex $parts end]]"
} else {
set name $full
}
return [list $lat $lon $name]
}
proc weather_fetch {url} {
set tok ""
if {[catch {
set tok [http::geturl $url -timeout 10000]
set body [http::data $tok]
http::cleanup $tok
set tok ""
} err]} {
if {$tok ne ""} { catch {http::cleanup $tok} }
error "HTTP error: $err"
}
if {[catch {set data [json::json2dict $body]} err]} {
error "JSON parse error: $err"
}
return $data
}
proc weather_color_temp {temp_c} {
set temp_f [expr {$temp_c * 9.0 / 5.0 + 32.0}]
if {$temp_c < 10} { set col 12
} elseif {$temp_c < 20} { set col 08
} elseif {$temp_c < 30} { set col 07
} else { set col 04 }
return "\003${col}[format %.1f $temp_c]\u00b0C ([format %.1f $temp_f]\u00b0F)\003"
}
proc weather_wind_dir {deg} {
if {$deg eq {} || $deg eq "null"} { return "?" }
set dirs {N NE E SE S SW W NW}
return [lindex $dirs [expr {round($deg / 45.0) % 8}]]
}
proc weather_get {d key {default {}}} {
if {[dict exists $d $key]} { return [dict get $d $key] }
return $default
}
# --- Commands ---
proc pub:weather_register {nick uhost hand chan text} {
variable weather_locations
set query [weather_sanitize $text]
if {$query eq {}} {
puthelp "PRIVMSG $chan :Usage: !register_location <location or zip>"
return
}
set geo [weather_geocode $query]
if {![llength $geo]} {
puthelp "PRIVMSG $chan :Could not find location for: $query"
return
}
lassign $geo lat lon name
set weather_locations($nick) [list $lat $lon $name]
weather_save
puthelp "PRIVMSG $chan :Location for $nick registered as: $name ($lat, $lon)"
}
proc pub:weather_change {nick uhost hand chan text} {
variable weather_locations
if {![info exists weather_locations($nick)]} {
puthelp "PRIVMSG $chan :You do not have a registered location. Use !register_location <location> to register one."
return
}
set query [weather_sanitize $text]
if {$query eq {}} {
puthelp "PRIVMSG $chan :Usage: !change_location <new location or zip>"
return
}
set geo [weather_geocode $query]
if {![llength $geo]} {
puthelp "PRIVMSG $chan :Could not find location for: $query"
return
}
lassign $geo lat lon name
set weather_locations($nick) [list $lat $lon $name]
weather_save
puthelp "PRIVMSG $chan :Your location has been updated to: $name ($lat, $lon)"
}
proc pub:weather_unregister {nick uhost hand chan text} {
variable weather_locations
if {[info exists weather_locations($nick)]} {
unset weather_locations($nick)
weather_save
puthelp "PRIVMSG $chan :Your registered location has been removed."
} else {
puthelp "PRIVMSG $chan :You do not have a registered location."
}
}
proc pub:weather_current {nick uhost hand chan text} {
variable weather_locations
variable weather_api_key
set query [weather_sanitize $text]
if {$query eq {}} {
if {[info exists weather_locations($nick)]} {
lassign $weather_locations($nick) lat lon name
} else {
puthelp "PRIVMSG $chan :Usage: !w <location or zip> or register your location with !register_location <location>"
return
}
} else {
set geo [weather_geocode $query]
if {![llength $geo]} {
puthelp "PRIVMSG $chan :Could not find coordinates for: $query"
return
}
lassign $geo lat lon name
}
set url "https://api.pirateweather.net/forecast/${weather_api_key}/${lat},${lon}?units=si&exclude=minutely,hourly,alerts"
if {[catch {set data [weather_fetch $url]} err]} {
puthelp "PRIVMSG $chan :Error retrieving weather data: $err"
return
}
if {![dict exists $data currently]} {
puthelp "PRIVMSG $chan :Error from Pirate Weather: [weather_get $data message Unknown]"
return
}
set c [dict get $data currently]
set conditions [weather_get $c summary "N/A"]
set temp_c [expr {double([weather_get $c temperature 0])}]
set pressure [weather_get $c pressure 0]
set humidity [expr {int([weather_get $c humidity 0] * 100)}]
set clouds [expr {int([weather_get $c cloudCover 0] * 100)}]
set wind_ms [expr {double([weather_get $c windSpeed 0])}]
set wind_deg [weather_get $c windBearing {}]
set rain [weather_get $c precipIntensity 0]
set wind_kmh [expr {$wind_ms * 3.6}]
set wind_mph [expr {$wind_ms * 2.23694}]
set wind_str "[format %.1f $wind_kmh] km/h / [format %.1f $wind_mph] mph"
puthelp "PRIVMSG $chan :\002Location:\002 $name | \002Conditions:\002 $conditions | \002Temperature:\002 [weather_color_temp $temp_c] | \002Pressure:\002 $pressure hPa | \002Humidity:\002 ${humidity}% | \002Precip:\002 $rain mm/h | \002Wind:\002 $wind_str from [weather_wind_dir $wind_deg] | \002Cloud cover:\002 ${clouds}%"
}
proc pub:weather_forecast {nick uhost hand chan text} {
variable weather_locations
variable weather_api_key
set query [weather_sanitize $text]
if {$query eq {}} {
if {[info exists weather_locations($nick)]} {
lassign $weather_locations($nick) lat lon name
} else {
puthelp "PRIVMSG $chan :Usage: !f <location or zip> or register your location with !register_location <location>"
return
}
} else {
set geo [weather_geocode $query]
if {![llength $geo]} {
puthelp "PRIVMSG $chan :Could not find coordinates for: $query"
return
}
lassign $geo lat lon name
}
set url "https://api.pirateweather.net/forecast/${weather_api_key}/${lat},${lon}?units=si&exclude=currently,minutely,hourly,alerts"
if {[catch {set data [weather_fetch $url]} err]} {
puthelp "PRIVMSG $chan :Error retrieving forecast data: $err"
return
}
if {![dict exists $data daily] || ![dict exists [dict get $data daily] data]} {
puthelp "PRIVMSG $chan :Error from Pirate Weather: [weather_get $data message Unknown]"
return
}
set days [lrange [dict get [dict get $data daily] data] 0 3]
set parts {}
foreach day $days {
set weekday [clock format [dict get $day time] -format "%A"]
set condition [string trimright [weather_get $day summary "N/A"] "."]
set max_t [expr {double([weather_get $day temperatureHigh 0])}]
set min_t [expr {double([weather_get $day temperatureLow 0])}]
lappend parts "$weekday $condition [weather_color_temp $max_t] [weather_color_temp $min_t]"
}
puthelp "PRIVMSG $chan :\002Location:\002 $name :: [join $parts { :: }]"
}
proc pub:weather_change_trigger {nick uhost hand chan text} {
variable trig_w
variable trig_f
variable weather_flag
# Only bot owners (flag n) may use this command
if {![matchattr $hand n]} {
puthelp "PRIVMSG $chan :Sorry $nick, only bot owners can change triggers."
return
}
set args [split [weather_sanitize $text]]
if {[llength $args] != 2} {
puthelp "PRIVMSG $chan :Usage: !change_trigger <w|f> <newtrigger> (e.g. !change_trigger w !weather)"
return
}
set which [string tolower [lindex $args 0]]
set new [lindex $args 1]
# Trigger must start with ! and contain only safe characters
if {![regexp {^![A-Za-z0-9_-]+$} $new]} {
puthelp "PRIVMSG $chan :Invalid trigger. Must start with ! and contain only letters, numbers, _ or -."
return
}
switch -- $which {
w {
if {$new eq $trig_w} {
puthelp "PRIVMSG $chan :Weather trigger is already set to: $trig_w"
return
}
bind pub $weather_flag $new ::CTweather::pub:weather_current
unbind pub $weather_flag $trig_w ::CTweather::pub:weather_current
set trig_w $new
puthelp "PRIVMSG $chan :Weather trigger changed to: $new"
}
f {
if {$new eq $trig_f} {
puthelp "PRIVMSG $chan :Forecast trigger is already set to: $trig_f"
return
}
bind pub $weather_flag $new ::CTweather::pub:weather_forecast
unbind pub $weather_flag $trig_f ::CTweather::pub:weather_forecast
set trig_f $new
puthelp "PRIVMSG $chan :Forecast trigger changed to: $new"
}
default {
puthelp "PRIVMSG $chan :Unknown trigger type '$which'. Use w (weather) or f (forecast)."
}
}
}
# --- Init ---
weather_load
putlog "ct-weather.tcl loaded."
}