├── lib ├── fmt │ └── __init__.py ├── view │ ├── __init__.py │ ├── moon.py │ └── prometheus.py ├── airports.py ├── weather_data.py ├── datasource │ └── README.md ├── extract_emoji.py └── buttons.py ├── test ├── proxy-data │ └── data1.headers └── query.sh ├── share ├── we-lang │ └── go.mod ├── emoji │ ├── ☀️.png │ ├── ☁️.png │ ├── ⛅️.png │ ├── ⛈.png │ ├── ✨.png │ ├── ❄️.png │ ├── 🌑.png │ ├── 🌒.png │ ├── 🌓.png │ ├── 🌔.png │ ├── 🌕.png │ ├── 🌖.png │ ├── 🌗.png │ ├── 🌘.png │ ├── 🌦.png │ ├── 🌧.png │ ├── 🌨.png │ ├── 🌩.png │ └── 🌫.png ├── static │ ├── favicon.ico │ ├── example-wttr-v2.png │ ├── example-tmux-status-line.png │ ├── style.css │ └── malformed-response.html ├── salt │ ├── pillar.sls │ ├── wttr.service │ ├── start.sh │ ├── README.md │ ├── wegorc │ └── init.sls ├── blacklist ├── screenrc ├── scripts │ ├── start-screen.sh │ └── clean-cache.sh ├── templates │ └── index.html ├── docker │ └── supervisord.conf ├── bash-function.txt ├── aliases ├── translations │ ├── zh-tw-help.txt │ ├── zh-cn-help.txt │ ├── eu.txt │ ├── es.txt │ ├── nb-help.txt │ ├── dk-help.txt │ ├── da-help.txt │ ├── th-help.txt │ ├── et-help.txt │ ├── vi-help.txt │ ├── id-help.txt │ ├── lv-help.txt │ ├── af-help.txt │ ├── mk.txt │ ├── ar-help.txt │ ├── ro-help.txt │ ├── de-help.txt │ ├── kk-help.txt │ ├── ru-help.txt │ ├── uk-help.txt │ ├── tr.txt │ ├── zh-tw.txt │ ├── bg-help.txt │ ├── am-help.txt │ ├── be-help.txt │ ├── eu-help.txt │ ├── fy.txt │ ├── pt-br-help.txt │ ├── pt-help.txt │ ├── cs-help.txt │ ├── ia-help.txt │ ├── ca-help.txt │ ├── hu-help.txt │ ├── oc-help.txt │ ├── ja.txt │ ├── fr-help.txt │ ├── pl-help.txt │ ├── lt-help.txt │ ├── nl-help.txt │ ├── am.txt │ ├── zh-cn.txt │ ├── en.txt │ ├── ia.txt │ ├── te.txt │ ├── ga.txt │ ├── tr-help.txt │ ├── mg-help.txt │ ├── el.txt │ ├── fa-help.txt │ ├── cy.txt │ ├── es-help.txt │ ├── vi.txt │ ├── hr.txt │ ├── af.txt │ ├── ca.txt │ ├── da.txt │ ├── eo.txt │ ├── id.txt │ ├── nb.txt │ ├── nn.txt │ ├── bs.txt │ ├── el-help.txt │ └── sl.txt └── help.txt ├── .gitignore ├── requirements.txt ├── .travis.yml ├── cmd ├── stat.go ├── srv.go └── peakHandling.go ├── Dockerfile ├── bin └── srv.py └── doc └── terminal-images.md /lib/fmt/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/view/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/proxy-data/data1.headers: -------------------------------------------------------------------------------- 1 | {"Content-Type": "application/json"} -------------------------------------------------------------------------------- /share/we-lang/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/chubin/wttr.in/v2 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /share/emoji/☀️.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/☀️.png -------------------------------------------------------------------------------- /share/emoji/☁️.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/☁️.png -------------------------------------------------------------------------------- /share/emoji/⛅️.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/⛅️.png -------------------------------------------------------------------------------- /share/emoji/⛈.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/⛈.png -------------------------------------------------------------------------------- /share/emoji/✨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/✨.png -------------------------------------------------------------------------------- /share/emoji/❄️.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/❄️.png -------------------------------------------------------------------------------- /share/emoji/🌑.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌑.png -------------------------------------------------------------------------------- /share/emoji/🌒.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌒.png -------------------------------------------------------------------------------- /share/emoji/🌓.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌓.png -------------------------------------------------------------------------------- /share/emoji/🌔.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌔.png -------------------------------------------------------------------------------- /share/emoji/🌕.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌕.png -------------------------------------------------------------------------------- /share/emoji/🌖.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌖.png -------------------------------------------------------------------------------- /share/emoji/🌗.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌗.png -------------------------------------------------------------------------------- /share/emoji/🌘.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌘.png -------------------------------------------------------------------------------- /share/emoji/🌦.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌦.png -------------------------------------------------------------------------------- /share/emoji/🌧.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌧.png -------------------------------------------------------------------------------- /share/emoji/🌨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌨.png -------------------------------------------------------------------------------- /share/emoji/🌩.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌩.png -------------------------------------------------------------------------------- /share/emoji/🌫.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/emoji/🌫.png -------------------------------------------------------------------------------- /share/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/static/favicon.ico -------------------------------------------------------------------------------- /share/salt/pillar.sls: -------------------------------------------------------------------------------- 1 | wttr: 2 | apikey: insert-api-key-here-and-make-this-pillar-available-to-salt 3 | -------------------------------------------------------------------------------- /share/static/example-wttr-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/static/example-wttr-v2.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ve/ 2 | share/static/fonts/ 3 | *.pyc 4 | data/ 5 | log/ 6 | .idea/ 7 | *.swp 8 | *.mmdb 9 | *.dat 10 | -------------------------------------------------------------------------------- /share/static/example-tmux-status-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skyme5/wttr.in/master/share/static/example-tmux-status-line.png -------------------------------------------------------------------------------- /share/blacklist: -------------------------------------------------------------------------------- 1 | NOT_FOUND 2 | apple-touch-icon.png 3 | apple-touch-icon-precomposed.png 4 | apple-touch-icon-152x152-precomposed.png 5 | 6 | -------------------------------------------------------------------------------- /share/screenrc: -------------------------------------------------------------------------------- 1 | screen -t srv.py bash -c "cd ~/wttr.in; ve/bin/python bin/srv.py; bash -i" 2 | screen -t proxy.py bash -c "cd ~/wttr.in; ve/bin/python bin/proxy.py; bash -i" 3 | -------------------------------------------------------------------------------- /share/scripts/start-screen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SESSION_NAME=wttr.in 4 | SCREENRC_PATH=$(dirname $(dirname "$0"))/screenrc 5 | 6 | screen -dmS "$SESSION_NAME" -c "$SCREENRC_PATH" 7 | 8 | -------------------------------------------------------------------------------- /share/salt/wttr.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Wttr weather service 3 | 4 | [Service] 5 | ExecStart=/usr/bin/authbind --deep /srv/ephemeral/start.sh 6 | Restart=always 7 | 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /share/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 |
8 | {{ body }}
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/share/scripts/clean-cache.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | CACHEDIR="/wttr.in/cache"
4 |
5 | for dir in wego proxy-wwo png
6 | do
7 | mv "${CACHEDIR}/${dir}" "${CACHEDIR}/${dir}.old"
8 | mkdir "${CACHEDIR}/${dir}"
9 | rm -rf "${CACHEDIR}/${dir}.old"
10 | done
11 |
12 | cd /wttr.in/log
13 | mv main.log main.log.1
14 | touch main.log
15 |
16 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | flask
2 | geoip2
3 | geopy
4 | requests
5 | gevent
6 | dnspython
7 | pylint
8 | cyrtranslit
9 | astral
10 | timezonefinder==2.1.2
11 | pytz
12 | pyte
13 | python-dateutil
14 | diagram
15 | pyjq
16 | scipy
17 | numpy
18 | pillow
19 | babel
20 | pylru
21 | pysocks
22 | supervisor
23 | numba
24 | emoji
25 | grapheme
26 | pycountry
27 |
--------------------------------------------------------------------------------
/share/salt/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | export WEGORC="/srv/ephemeral/.wegorc"
3 | export GOPATH="/srv/ephemeral"
4 |
5 | export WTTR_MYDIR="/srv/ephemeral/wttr.in"
6 | export WTTR_GEOLITE="/srv/ephemeral/GeoLite2-City.mmdb"
7 | export WTTR_WEGO="$GOPATH/bin/wego"
8 |
9 | export WTTR_LISTEN_HOST="0.0.0.0"
10 | export WTTR_LISTEN_PORT="80"
11 |
12 | python $WTTR_MYDIR/bin/srv.py
13 |
--------------------------------------------------------------------------------
/lib/airports.py:
--------------------------------------------------------------------------------
1 | import csv
2 |
3 | AIRPORTS_DAT_FILE = '/home/igor/wttrin-geo/share/airports.dat'
4 |
5 | def load_aiports_index():
6 | file_ = open(AIRPORTS_DAT_FILE, "r")
7 | reader = csv.reader(file_)
8 | airport_index = {}
9 |
10 | for line in reader:
11 | airport_index[line[4]] = line
12 |
13 | return airport_index
14 |
15 | AIRPORTS_INDEX = load_aiports_index()
16 |
17 | def get_airport_gps_location(iata_code):
18 | if iata_code in AIRPORTS_INDEX:
19 | airport = AIRPORTS_INDEX[iata_code]
20 | return '%s,%s airport' % (airport[6], airport[7]) #, airport[1])
21 | return None
22 |
23 |
--------------------------------------------------------------------------------
/share/static/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: black;
3 | color: #bbbbbb;
4 | }
5 | pre {
6 | /* font-family: source_code_proregular; */
7 |
8 | /*
9 | font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace;
10 | font-size: 70%;
11 | */
12 |
13 | /*font-family: Lucida Console,Lucida Sans Typewriter,monaco,Bitstream Vera Sans Mono,monospace; */
14 | /*Droid Sans Mono*/
15 | font-family: "Source Code Pro", "DejaVu Sans Mono", Menlo, "Lucida Sans Typewriter", "Lucida Console", monaco, "Bitstream Vera Sans Mono", monospace;
16 | /*font-family: bitstream_vera_sans_monoroman;*/
17 | font-size: 75%;
18 | }
19 |
--------------------------------------------------------------------------------
/lib/weather_data.py:
--------------------------------------------------------------------------------
1 | """
2 | Weather data source
3 | """
4 |
5 | import json
6 | import requests
7 | from globals import WWO_KEY
8 |
9 | def get_weather_data(location, lang):
10 | """
11 | Get weather data for `location`
12 | """
13 | key = WWO_KEY
14 | url = ('/premium/v1/weather.ashx'
15 | '?key=%s&q=%s&format=json'
16 | '&num_of_days=3&tp=3&lang=%s') % (key, location, lang)
17 | url = 'http://127.0.0.1:5001' + url
18 |
19 | response = requests.get(url, timeout=10)
20 | try:
21 | data = json.loads(response.content)
22 | except ValueError:
23 | data = {}
24 | return data
25 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | group: travis_latest
2 | language: python
3 | cache: pip
4 | python:
5 | - 3.7
6 | install:
7 | - pip install flake8 -r requirements.txt
8 | before_script:
9 | # stop the build if there are Python syntax errors or undefined names
10 | - flake8 bin lib --count --select=E9,F63,F7,F82 --show-source --statistics
11 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
12 | - flake8 bin lib --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
13 | script:
14 | - true # pytest --capture=sys # add other tests here
15 | notifications:
16 | on_success: change
17 | on_failure: change # `always` will be the setting once code changes slow down.
18 |
--------------------------------------------------------------------------------
/share/docker/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | nodaemon=true
3 | logfile=/var/log/supervisor/supervisord.log
4 | pidfile=/var/run/supervisord.pid
5 |
6 | [program:srv]
7 | command=python3 /app/bin/srv.py
8 | stderr_logfile=/var/log/supervisor/srv-stderr.log
9 | stdout_logfile=/var/log/supervisor/srv-stdout.log
10 |
11 | [program:proxy]
12 | command=python3 /app/bin/proxy.py
13 | stderr_logfile=/var/log/supervisor/proxy-stderr.log
14 | stdout_logfile=/var/log/supervisor/proxy-stdout.log
15 |
16 | [program:geoproxy]
17 | command=python3 /app/bin/geo-proxy.py
18 | stderr_logfile=/var/log/supervisor/geoproxy-stderr.log
19 | stdout_logfile=/var/log/supervisor/geoproxy-stdout.log
20 |
21 | [include]
22 | files=/etc/supervisor/conf.d/*.conf
23 |
--------------------------------------------------------------------------------
/share/salt/README.md:
--------------------------------------------------------------------------------
1 | # Opinionated example of deployment via Salt Stack
2 |
3 | ## Assumptions:
4 | * user & group srv:srv exist, this is used as a generic service runner
5 | * you want to run the service on port 80, directly exposed to the interwebs (you really want to add a reverse SSL proxy in between)
6 | * You have, or are willing to deploy Salt Stack.
7 | * A bit of assembly is required since you need to move pillar.sls into your saltroot/pillar/ and the rest into saltroot/wttr/
8 | * You want metric-sm units. Just roll your own wegorc to change this
9 |
10 | ## Caveats:
11 | * Doesn't do enough to make a recent master checkout work, i.e. needs further improvement. Latest known working revision is 0d76ba4a3e112694665af6653040807835883b22
12 |
--------------------------------------------------------------------------------
/cmd/stat.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "sync"
6 | "time"
7 | )
8 |
9 | type safeCounter struct {
10 | v map[int]int
11 | mux sync.Mutex
12 | }
13 |
14 | func (c *safeCounter) inc(key int) {
15 | c.mux.Lock()
16 | c.v[key]++
17 | c.mux.Unlock()
18 | }
19 |
20 | // func (c *safeCounter) val(key int) int {
21 | // c.mux.Lock()
22 | // defer c.mux.Unlock()
23 | // return c.v[key]
24 | // }
25 | //
26 | // func (c *safeCounter) reset(key int) int {
27 | // c.mux.Lock()
28 | // defer c.mux.Unlock()
29 | // result := c.v[key]
30 | // c.v[key] = 0
31 | // return result
32 | // }
33 |
34 | var queriesPerMinute safeCounter
35 |
36 | func printStat() {
37 | _, min, _ := time.Now().Clock()
38 | queriesPerMinute.inc(min)
39 | log.Printf("Processed %d requests\n", min)
40 | }
41 |
--------------------------------------------------------------------------------
/lib/datasource/README.md:
--------------------------------------------------------------------------------
1 |
2 | Currently wttr.in uses just one data source, but more data sources must be added.
3 | Having more data sources will increase data quality, and make it possible
4 | to select data source basing on location, or on the user's preferences.
5 |
6 | ## Possible data sources
7 |
8 | * [Open weather map](https://openweathermap.org/)
9 | * [Accu weather](https://www.accuweather.com/)
10 | * [Windy](https://www.windy.com/?26.953,75.711,5)
11 | * [Yr](https://www.yr.no/nb)
12 | * [BBC WeatherFeeds](https://support.bbc.co.uk/platform/feeds/WeatherFeeds.htm)
13 | * https://weather.gc.ca
14 | * [Bom](http://www.bom.gov.au)
15 | * [IMD](https://mausam.imd.gov.in/)
16 | * [darksky](https://darksky.net/forecast/40.7127,-74.0059/us12/en)
17 | * [weather bug](https://www.weatherbug.com/)
18 | * [weather underground](https://www.wunderground.com/)
19 |
--------------------------------------------------------------------------------
/share/bash-function.txt:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env bash
2 | # If you source this file, it will set WTTR_PARAMS as well as show weather.
3 |
4 | # WTTR_PARAMS is space-separated URL parameters, many of which are single characters that can be
5 | # lumped together. For example, "F q m" behaves the same as "Fqm".
6 | if [[ -z "$WTTR_PARAMS" ]]; then
7 | # Form localized URL parameters for curl
8 | if [[ -t 1 ]] && [[ "$(tput cols)" -lt 125 ]]; then
9 | WTTR_PARAMS+='n'
10 | fi 2> /dev/null
11 | for _token in $( locale LC_MEASUREMENT ); do
12 | case $_token in
13 | 1) WTTR_PARAMS+='m' ;;
14 | 2) WTTR_PARAMS+='u' ;;
15 | esac
16 | done 2> /dev/null
17 | unset _token
18 | export WTTR_PARAMS
19 | fi
20 |
21 | wttr() {
22 | local location="${1// /+}"
23 | command shift
24 | local args=""
25 | for p in $WTTR_PARAMS "$@"; do
26 | args+=" --data-urlencode $p "
27 | done
28 | curl -fGsS -H "Accept-Language: ${LANG%_*}" $args --compressed "wttr.in/${location}"
29 | }
30 |
31 | wttr "$@"
32 |
--------------------------------------------------------------------------------
/lib/extract_emoji.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #vim: fileencoding=utf-8
3 |
4 | """
5 |
6 | At the moment, Pillow library does not support colorful emojis,
7 | that is why emojis must be extracted to external files first,
8 | and then they must be handled as usual graphical objects
9 | and not as text.
10 |
11 | The files are extracted using Imagemagick.
12 |
13 | Usage:
14 |
15 | ve/bi/python lib/extract_emoji.py
16 | """
17 |
18 | import subprocess
19 |
20 | EMOJIS = [
21 | "✨",
22 | "☁️",
23 | "🌫",
24 | "🌧",
25 | "🌧",
26 | "❄️",
27 | "❄️",
28 | "🌦",
29 | "🌦",
30 | "🌧",
31 | "🌧",
32 | "🌨",
33 | "🌨",
34 | "⛅️",
35 | "☀️",
36 | "🌩",
37 | "⛈",
38 | "⛈",
39 | "☁️",
40 | "🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"
41 | ]
42 |
43 | def extract_emojis_to_directory(dirname):
44 | """
45 | Extract emoji from an emoji font, to separate files.
46 | """
47 |
48 | emoji_font = "Noto Color Emoji"
49 | emoji_size = 30
50 |
51 | for emoji in EMOJIS:
52 | filename = "%s/%s.png" % (dirname, emoji)
53 | convert_string = [
54 | "convert", "-background", "black", "-size", "%sx%s" % (emoji_size, emoji_size),
55 | "-set", "colorspace", "sRGB",
56 | "pango:%s" % (emoji_font, emoji),
57 | filename
58 | ]
59 | subprocess.Popen(convert_string)
60 |
61 | if __name__ == '__main__':
62 | extract_emojis_to_directory("share/emoji")
63 |
--------------------------------------------------------------------------------
/share/aliases:
--------------------------------------------------------------------------------
1 | Msk : Moscow
2 | Moskva : Moscow
3 | Moskau : Moscow
4 | Kyiv : Kiev,Ukraine
5 | Kiew : Kiev,Ukraine
6 | Kiev : Kiev,Ukraine
7 | Kijev : Kiev
8 | Kharkov : Kharkiv
9 | spb : Saint Petersburg
10 | Dnipro : Dnipropetrovsk
11 | st.petersburg: Saint Petersburg
12 | sanfrancisco: San Francisco
13 | san fransisco:San Francisco
14 | San+Francisco:San Francisco
15 | San-Francisco:San Francisco
16 | Dnipropetrovsk:Dnepropetrovsk
17 | Dnepr : Dnipropetrovsk
18 | sanjose : San Jose
19 | san josé : San Jose
20 | newyork : New York
21 | newy-yourk : New York
22 | new-york : New York
23 | Saint-Petersburg: Saint Petersburg
24 | Krakow : Krakow,pl
25 | indonesia : Jakarta
26 | hanoi,vietnam:Hanoi
27 | nuernberg : Nuremberg
28 | nürnberg : Nuremberg
29 | norway : Oslo
30 | ville de bruxelles - stad brussel: Brussels
31 | frankfurt am main:Frankfurt, Hessen
32 | frankfurt :Frankfurt, Hessen
33 | frankfurt oder : Frankfurt, Brandenburg
34 | frankfurt (oder): Frankfurt, Brandenburg
35 | tel-aviv : Tel Aviv
36 | sao paulo : São Paulo
37 | los-angeles : Los Angeles
38 | Sevastopol : Sevastopol, Ukraine
39 | Simferopol : Simferopol, Ukraine
40 | Beersheva : Beersheba
41 | Be'ersheva : Beersheba
42 | Be'er Sheva : Beersheba
43 | Lugansk : Luhansk
44 | Bjalistoko : Białystok
45 | Chicago : Chicago,IL
46 | Paris : Paris,France
47 | Giessen : Giessen, Germany
48 | Braga : Braga, Portugal
49 | Kashan : ~Kashan,Iran
50 | Baku : Baku,Az
51 | Rome : Rome, Italia
52 | YYZ : Toronto Pearson Airport
53 |
--------------------------------------------------------------------------------
/lib/view/moon.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | import os
4 | import dateutil.parser
5 |
6 | from gevent.subprocess import Popen, PIPE
7 |
8 | sys.path.insert(0, "..")
9 | import constants
10 | import parse_query
11 | import globals
12 |
13 | def get_moon(parsed_query):
14 |
15 | location = parsed_query['orig_location']
16 | html = parsed_query['html_output']
17 | lang = parsed_query['lang']
18 | hemisphere = parsed_query['hemisphere']
19 |
20 | date = None
21 | if '@' in location:
22 | date = location[location.index('@')+1:]
23 | location = location[:location.index('@')]
24 |
25 | cmd = [globals.PYPHOON]
26 | if lang:
27 | cmd += ["-l", lang]
28 |
29 | if not hemisphere:
30 | cmd += ["-s", "south"]
31 |
32 | if date:
33 | try:
34 | dateutil.parser.parse(date)
35 | except Exception as e:
36 | print("ERROR: %s" % e)
37 | else:
38 | cmd += [date]
39 |
40 | p = Popen(cmd, stdout=PIPE, stderr=PIPE)
41 | stdout = p.communicate()[0]
42 | stdout = stdout.decode("utf-8")
43 |
44 | if parsed_query.get('no-terminal', False):
45 | stdout = globals.remove_ansi(stdout)
46 |
47 | if html:
48 | p = Popen(
49 | ["bash", globals.ANSI2HTML, "--palette=solarized", "--bg=dark"],
50 | stdin=PIPE, stdout=PIPE, stderr=PIPE)
51 | stdout, stderr = p.communicate(stdout.encode("utf-8"))
52 | stdout = stdout.decode("utf-8")
53 | stderr = stderr.decode("utf-8")
54 | if p.returncode != 0:
55 | globals.error(stdout + stderr)
56 |
57 | return stdout
58 |
--------------------------------------------------------------------------------
/cmd/srv.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "log"
6 | "net"
7 | "net/http"
8 | "time"
9 |
10 | lru "github.com/hashicorp/golang-lru"
11 | )
12 |
13 | const uplinkSrvAddr = "127.0.0.1:9002"
14 | const uplinkTimeout = 30
15 | const prefetchInterval = 300
16 | const lruCacheSize = 12800
17 |
18 | var lruCache *lru.Cache
19 |
20 | type responseWithHeader struct {
21 | InProgress bool // true if the request is being processed
22 | Expires time.Time // expiration time of the cache entry
23 |
24 | Body []byte
25 | Header http.Header
26 | StatusCode int // e.g. 200
27 | }
28 |
29 | func init() {
30 | var err error
31 | lruCache, err = lru.New(lruCacheSize)
32 | if err != nil {
33 | panic(err)
34 | }
35 |
36 | dialer := &net.Dialer{
37 | Timeout: uplinkTimeout * time.Second,
38 | KeepAlive: uplinkTimeout * time.Second,
39 | DualStack: true,
40 | }
41 |
42 | http.DefaultTransport.(*http.Transport).DialContext = func(ctx context.Context, network, _ string) (net.Conn, error) {
43 | return dialer.DialContext(ctx, network, uplinkSrvAddr)
44 | }
45 |
46 | initPeakHandling()
47 | }
48 |
49 | func copyHeader(dst, src http.Header) {
50 | for k, vv := range src {
51 | for _, v := range vv {
52 | dst.Add(k, v)
53 | }
54 | }
55 | }
56 |
57 | func main() {
58 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
59 | // printStat()
60 | response := processRequest(r)
61 |
62 | copyHeader(w.Header(), response.Header)
63 | w.Header().Set("Access-Control-Allow-Origin", "*")
64 | w.WriteHeader(response.StatusCode)
65 | w.Write(response.Body)
66 | })
67 |
68 | log.Fatal(http.ListenAndServe(":8082", nil))
69 | }
70 |
--------------------------------------------------------------------------------
/test/query.sh:
--------------------------------------------------------------------------------
1 | queries=(
2 | /
3 | /Kiev
4 | /Kiev.png
5 | /?T
6 | /Киев
7 | /Kiev?2
8 | "/Kiev?format=1"
9 | "/Kiev?format=2"
10 | "/Kiev?format=3"
11 | "/Kiev?format=4"
12 | "/Kiev?format=v2"
13 | "/Kiev?format=%s"
14 | "/Kiev?format=%S"
15 | "/Kiev?format=%D+%S+%z+%s+%d"
16 | "/:help"
17 | "/Kiev?T"
18 | "/Kiev?p"
19 | "/Kiev?q"
20 | "/Kiev?Q"
21 | "/Kiev_text=no_view=v2.png"
22 | "/Kiev.png?1nqF"
23 | "/Kiev_1nqF.png"
24 | )
25 |
26 | options=$(cat <8 | Sorry, we processed more than 1M requests today and we ran out of our datasource capacity. 9 | We hope to solve the problem as soon as possible, so you can enjoy 10 | your favourite weather service 24x365 even if it rains or snows. 11 | 12 | We will solve the problem as soon as possible. 13 | Follow @igor_chubin for the updates. 14 | 15 | If you like to code (and you surely do), you can check the wttr.in repository 16 | to see how the scalability problem is (not yet) solved. 17 | 18 |19 |
20 | 21 | 22 | 23 |How do you check the weather? curl wttr.in — Sure thing! #wttrin pic.twitter.com/mgYzW2ajyq
— Igor Chubin (@igor_chubin) February 20, 2016
', 30 | (TWITTER_BUTTON 31 | + GITHUB_BUTTON 32 | + GITHUB_BUTTON_3 33 | + GITHUB_BUTTON_2 34 | + GITHUB_BUTTON_FOOTER) + '') 35 | -------------------------------------------------------------------------------- /share/static/malformed-response.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 | 6 |
24 |