Repository: chubin/wttr.in Branch: master Commit: 771d003db43b Files: 222 Total size: 1.0 MB Directory structure: gitextract_3puwzenn/ ├── .flake8 ├── .github/ │ └── workflows/ │ └── makefile.yml ├── .gitignore ├── .golangci.yaml ├── .travis.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bin/ │ ├── geo-proxy.py │ ├── proxy.py │ └── srv.py ├── config/ │ └── services/ │ └── services.yaml ├── doc/ │ ├── integrations.md │ └── terminal-images.md ├── go.mod ├── go.sum ├── internal/ │ ├── config/ │ │ └── config.go │ ├── fmt/ │ │ └── png/ │ │ ├── colors.go │ │ ├── go.mod │ │ ├── go.sum │ │ └── png.go │ ├── geo/ │ │ ├── ip/ │ │ │ ├── convert.go │ │ │ ├── ip.go │ │ │ └── ip_test.go │ │ └── location/ │ │ ├── cache.go │ │ ├── convert.go │ │ ├── location.go │ │ ├── nominatim.go │ │ ├── nominatim_locationiq.go │ │ ├── nominatim_opencage.go │ │ ├── response.go │ │ └── search.go │ ├── logging/ │ │ ├── logging.go │ │ └── suppress.go │ ├── options/ │ │ ├── options.go │ │ ├── parse.go │ │ └── processlog.go │ ├── processor/ │ │ ├── j1.go │ │ ├── peak.go │ │ └── processor.go │ ├── routing/ │ │ └── routing.go │ ├── stats/ │ │ └── stats.go │ ├── types/ │ │ ├── errors.go │ │ └── types.go │ ├── util/ │ │ ├── files.go │ │ ├── http.go │ │ └── yaml.go │ └── view/ │ └── v1/ │ ├── api.go │ ├── cmd.go │ ├── format.go │ ├── icons.go │ ├── locale.go │ └── view1.go ├── lib/ │ ├── airports.py │ ├── buttons.py │ ├── cache.py │ ├── constants.py │ ├── datasource/ │ │ └── README.md │ ├── duplicate_translations.py │ ├── extract_emoji.py │ ├── fields.py │ ├── fmt/ │ │ ├── __init__.py │ │ ├── png.py │ │ └── unicodedata2.py │ ├── globals.py │ ├── limits.py │ ├── location.py │ ├── metno.py │ ├── parse_query.py │ ├── proxy_log.py │ ├── translations.py │ ├── translations_v2.py │ ├── view/ │ │ ├── __init__.py │ │ ├── line.py │ │ ├── moon.py │ │ ├── prometheus.py │ │ ├── v2.py │ │ └── wttr.py │ ├── weather_data.py │ └── wttr_srv.py ├── requirements.txt ├── share/ │ ├── aliases │ ├── ansi2html.sh │ ├── bash-function.txt │ ├── blacklist │ ├── docker/ │ │ └── supervisord.conf │ ├── help.txt │ ├── iterm2.txt │ ├── list-of-iata-codes.txt │ ├── salt/ │ │ ├── README.md │ │ ├── init.sls │ │ ├── pillar.sls │ │ ├── start.sh │ │ ├── wegorc │ │ └── wttr.service │ ├── screenrc │ ├── scripts/ │ │ ├── build-welang.sh │ │ ├── clean-cache.sh │ │ ├── log-space.sh │ │ └── start-screen.sh │ ├── static/ │ │ ├── malformed-response.html │ │ └── style.css │ ├── systemd/ │ │ ├── README.md │ │ ├── wttrin.service │ │ └── wttrin.sh │ ├── templates/ │ │ └── index.html │ ├── test-thunder.txt │ ├── translation.txt │ └── translations/ │ ├── af-help.txt │ ├── af.txt │ ├── am-help.txt │ ├── am.txt │ ├── ar-help.txt │ ├── ar.txt │ ├── az.txt │ ├── be-help.txt │ ├── be.txt │ ├── bg-help.txt │ ├── bn-help.txt │ ├── bn.txt │ ├── bs.txt │ ├── ca-help.txt │ ├── ca.txt │ ├── cs-help.txt │ ├── cs.txt │ ├── cy.txt │ ├── da-help.txt │ ├── da.txt │ ├── de-help.txt │ ├── de.txt │ ├── dk-help.txt │ ├── el-help.txt │ ├── el.txt │ ├── en.txt │ ├── eo.txt │ ├── es-help.txt │ ├── es.txt │ ├── et-help.txt │ ├── et.txt │ ├── eu-help.txt │ ├── eu.txt │ ├── fa-help.txt │ ├── fa.txt │ ├── fr-help.txt │ ├── fr.txt │ ├── fy.txt │ ├── ga.txt │ ├── gl-help.txt │ ├── gl.txt │ ├── gu-help.txt │ ├── gu.txt │ ├── he.txt │ ├── hi-help.txt │ ├── hi.txt │ ├── hr.txt │ ├── hu-help.txt │ ├── hu.txt │ ├── hy.txt │ ├── ia-help.txt │ ├── ia.txt │ ├── id-help.txt │ ├── id.txt │ ├── is.txt │ ├── it-help.txt │ ├── it.txt │ ├── ja.txt │ ├── kk-help.txt │ ├── kk.txt │ ├── lt-help.txt │ ├── lt.txt │ ├── lv-help.txt │ ├── lv.txt │ ├── messages/ │ │ ├── en.yaml │ │ └── gu.yaml │ ├── mg-help.txt │ ├── mg.txt │ ├── mk.txt │ ├── mr-help.txt │ ├── mr.txt │ ├── nb-help.txt │ ├── nb.txt │ ├── nl-help.txt │ ├── nl.txt │ ├── nn.txt │ ├── oc-help.txt │ ├── oc.txt │ ├── pl-help.txt │ ├── pl.txt │ ├── pt-br-help.txt │ ├── pt-br.txt │ ├── pt-help.txt │ ├── pt.txt │ ├── ro-help.txt │ ├── ro.txt │ ├── ru-help.txt │ ├── ru.txt │ ├── sl.txt │ ├── ta-help.txt │ ├── ta.txt │ ├── te-help.txt │ ├── te.txt │ ├── th-help.txt │ ├── th.txt │ ├── tr-help.txt │ ├── tr.txt │ ├── uk-help.txt │ ├── uk.txt │ ├── ukr-help.txt │ ├── uz.txt │ ├── vi-help.txt │ ├── vi.txt │ ├── zh-cn-help.txt │ ├── zh-cn.txt │ ├── zh-tw-help.txt │ └── zh-tw.txt ├── spec/ │ └── options/ │ └── options.yaml ├── srv.go └── test/ ├── proxy-data/ │ ├── data1 │ └── data1.headers ├── query.sh └── test-data/ └── signatures ================================================ FILE CONTENTS ================================================ ================================================ FILE: .flake8 ================================================ [flake8] ignore = E402,E501 ================================================ FILE: .github/workflows/makefile.yml ================================================ name: Makefile CI on: push: branches: [ "master" ] pull_request: branches: [ "master" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Set up Python environment - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.11' # Specify the Python version you need (3.11 required for pyjq) - name: Create and activate virtual environment run: | python -m venv venv source venv/bin/activate echo "Virtual environment created and activated." - name: Install Python dependencies run: | sudo apt-get install libjq-dev source venv/bin/activate pip install -r requirements.txt - name: Add Python virtual environment to PATH run: | echo "${{ github.workspace }}/venv/bin" >> $GITHUB_PATH # Set up Go environment - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.24' # Replace with the needed Go version - name: Install gofumpt run: | go install mvdan.cc/gofumpt@latest - name: Install goimports run: | go install golang.org/x/tools/cmd/goimports@latest - name: Install swagger run: | cd /tmp \ && go install github.com/go-swagger/go-swagger/cmd/swagger@latest - name: Add Go bin to PATH run: | echo "${{ runner.temp }}/go/bin" >> $GITHUB_PATH - name: Install dependencies run: make - name: Run check run: make check ================================================ FILE: .gitignore ================================================ ve/ share/static/fonts/ *.pyc data/ log/ .idea/ *.swp *.mmdb *.dat ================================================ FILE: .golangci.yaml ================================================ run: skip-dirs: - pkg/curlator linters: enable-all: true disable: - wsl - wrapcheck - varnamelen - gci - exhaustivestruct - exhaustruct - gomnd - gofmt # to be fixed: - ireturn - gosec - noctx - interfacer # deprecated: - scopelint - deadcode - varcheck - maligned - ifshort - nosnakecase - structcheck - golint ================================================ FILE: .travis.yml ================================================ group: travis_latest language: python cache: pip python: - 3.7 install: - pip install flake8 -r requirements.txt before_script: # stop the build if there are Python syntax errors or undefined names - flake8 bin lib --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 bin lib --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics script: - true # pytest --capture=sys # add other tests here notifications: on_success: change on_failure: change # `always` will be the setting once code changes slow down. ================================================ FILE: Dockerfile ================================================ # Build stage FROM golang:1-alpine as builder WORKDIR /app COPY ./share/we-lang/ /app RUN apk add --no-cache git RUN go get -u github.com/mattn/go-colorable && \ go get -u github.com/klauspost/lctime && \ go get -u github.com/mattn/go-runewidth && \ cd /app && CGO_ENABLED=0 go build . # Application stage FROM alpine:3.21.1 WORKDIR /app COPY ./requirements.txt /app ENV LLVM_CONFIG=/usr/bin/llvm11-config RUN apk add --no-cache --virtual .build \ autoconf \ automake \ g++ \ gcc \ jpeg-dev \ llvm11-dev\ make \ zlib-dev \ && apk add --no-cache \ python3 \ py3-pip \ py3-scipy \ py3-wheel \ py3-gevent \ zlib \ jpeg \ llvm11 \ libtool \ supervisor \ py3-numpy-dev \ python3-dev && \ mkdir -p /app/cache && \ mkdir -p /var/log/supervisor && \ mkdir -p /etc/supervisor/conf.d && \ chmod -R o+rw /var/log/supervisor && \ chmod -R o+rw /var/run && \ pip install -r requirements.txt --no-cache-dir && \ apk del --no-cache -r .build COPY --from=builder /app/wttr.in /app/bin/wttr.in COPY ./bin /app/bin COPY ./lib /app/lib COPY ./share /app/share COPY share/docker/supervisord.conf /etc/supervisor/supervisord.conf ENV WTTR_MYDIR="/app" ENV WTTR_GEOLITE="/app/GeoLite2-City.mmdb" ENV WTTR_WEGO="/app/bin/wttr.in" ENV WTTR_LISTEN_HOST="0.0.0.0" ENV WTTR_LISTEN_PORT="8002" EXPOSE 8002 CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Makefile ================================================ srv: srv.go internal/*/*.go internal/*/*/*.go #go build -o srv -ldflags '-w -linkmode external -extldflags "-static"' ./ go build -o srv ./ check: true go-test: go test ./... lint: golangci-lint run ./... ================================================ FILE: README.md ================================================ *wttr.in — the right way to ~check~ `curl` the weather!* wttr.in is a console-oriented weather forecast service that supports various information representation methods like terminal-oriented ANSI-sequences for console HTTP clients (curl, httpie, or wget), HTML for web browsers, or PNG for graphical viewers. Originally started as a small project, a wrapper for [wego](https://github.com/schachmat/wego), intended to demonstrate the power of the console-oriented services, *wttr.in* became a popular weather reporting service, handling tens of millions[¹](#wttrin-usage-stats) of queries daily. You can see it running here: [wttr.in](https://wttr.in). [Documentation](https://wttr.in/:help) | [Usage](https://github.com/chubin/wttr.in#usage) | [One-line output](https://github.com/chubin/wttr.in#one-line-output) | [Data-rich output format](https://github.com/chubin/wttr.in#data-rich-output-format-v2) | [Map view](https://github.com/chubin/wttr.in#map-view-v3) | [Output formats](https://github.com/chubin/wttr.in#different-output-formats) | [Moon phases](https://github.com/chubin/wttr.in#moon-phases) | [Internationalization](https://github.com/chubin/wttr.in#internationalization-and-localization) | [Installation](https://github.com/chubin/wttr.in#installation) ## Usage You can access the service from a shell or from a Web browser like this: $ curl wttr.in Weather for City: Paris, France \ / Clear .-. 10 – 11 °C ― ( ) ― ↑ 11 km/h `-’ 10 km / \ 0.0 mm Here is an example weather report: ![Weather Report](share/pics/San_Francisco.png) Or in PowerShell: ```PowerShell Invoke-RestMethod https://wttr.in ``` Want to get the weather information for a specific location? You can add the desired location to the URL in your request like this: $ curl wttr.in/London $ curl wttr.in/Moscow $ curl wttr.in/Salt+Lake+City If you omit the location name, you will get the report for your current location based on your IP address. Use 3-letter airport codes in order to get the weather information at a certain airport: $ curl wttr.in/muc # Weather for IATA: muc, Munich International Airport, Germany $ curl wttr.in/ham # Weather for IATA: ham, Hamburg Airport, Germany Let's say you'd like to get the weather for a geographical location other than a town or city - maybe an attraction in a city, a mountain name, or some special location. Add the character `~` before the name to look up that special location name before the weather is then retrieved: $ curl wttr.in/~Vostok+Station $ curl wttr.in/~Eiffel+Tower $ curl wttr.in/~Kilimanjaro For these examples, you'll see a line below the weather forecast output that shows the geolocation results of looking up the location: Location: Vostok Station, станция Восток, AAT, Antarctica [-78.4642714,106.8364678] Location: Tour Eiffel, 5, Avenue Anatole France, Gros-Caillou, 7e, Paris, Île-de-France, 75007, France [48.8582602,2.29449905432] Location: Kilimanjaro, Northern, Tanzania [-3.4762789,37.3872648] You can also use IP-addresses (direct) or domain names (prefixed with `@`) to specify a location: $ curl wttr.in/@github.com $ curl wttr.in/@msu.ru To get detailed information online, you can access the [/:help](https://wttr.in/:help) page: $ curl wttr.in/:help ### Weather Units By default the USCS units are used for the queries from the USA and the metric system for the rest of the world. You can override this behavior by adding `?u`, `?m` or `?M` to a URL like this: $ curl wttr.in/Amsterdam?u # USCS (used by default in US) $ curl wttr.in/Amsterdam?m # metric (SI) (used by default everywhere except US) $ curl wttr.in/Amsterdam?M # metric (SI), but show wind speed in m/s If you have several options to pass, write them without delimiters in between for the one-letter options, and use `&` as a delimiter for the long options with values: $ curl 'wttr.in/Amsterdam?m2&lang=nl' It would be a rough equivalent of `-m2 --lang nl` for the GNU CLI syntax. ## Supported output formats and views wttr.in currently supports five output formats: * ANSI for the terminal; * Plain-text for the terminal and scripts; * HTML for the browser; * PNG for the graphical viewers; * JSON for scripts and APIs; * Prometheus metrics for scripts and APIs. The ANSI and HTML formats are selected based on the User-Agent string. To force plain text, which disables colors: $ curl wttr.in/?T To restrict output to glyphs available in standard console fonts (e.g. Consolas and Lucida Console): $ curl wttr.in/?d The PNG format can be forced by adding `.png` to the end of the query: $ wget wttr.in/Paris.png You can use all of the options with the PNG-format like in an URL, but you have to separate them with `_` instead of `?` and `&`: $ wget wttr.in/Paris_0tqp_lang=fr.png Useful options for the PNG format: * `t` for transparency (`transparency=150`); * transparency=0..255 for a custom transparency level. Transparency is a useful feature when weather PNGs are used to add weather data to pictures: $ convert source.jpg <( curl wttr.in/Oymyakon_tqp0.png ) -geometry +50+50 -composite target.jpg In this example: * `source.jpg` - source file; * `target.jpg` - target file; * `Oymyakon` - name of the location; * `tqp0` - options (recommended). ![Picture with weather data](https://pbs.twimg.com/media/C69-wsIW0AAcAD5.jpg) You can embed a special wttr.in widget, that displays the weather condition for the current or a selected location, into a HTML page using the [wttr-switcher](https://github.com/midzer/wttr-switcher). That is how it looks like: [wttr-switcher-example](https://midzer.github.io/wttr-switcher/) or on a real world web site: https://feuerwehr-eisolzried.de/. ![Embedded wttr.in example at feuerwehr-eisolzried.de](https://user-images.githubusercontent.com/3875145/65265457-50eac180-db11-11e9-8f9b-2e1711dfc436.png) ## One-line output One-line output format is convenient to be used to show weather info in status bar of different programs, such as *tmux*, *weechat*, etc. For one-line output format, specify additional URL parameter `format`: ``` $ curl wttr.in/Nuremberg?format=3 Nuremberg: 🌦 +11⁰C ``` Available preconfigured formats: 1, 2, 3, 4 and the custom format using the percent notation (see below). * 1: Current weather at location: `🌦 +11⁰C` * 2: Current weather at location with more details: `🌦 🌡️+11°C 🌬️↓4km/h` * 3: Name of location and current weather at location: `Nuremberg: 🌦 +11⁰C` * 4: Name of location and current weather at location with more details: `Nuremberg: 🌦 🌡️+11°C 🌬️↓4km/h` You can specify multiple locations separated with `:` (for repeating queries): ``` $ curl wttr.in/Nuremberg:Hamburg:Berlin?format=3 Nuremberg: 🌦 +11⁰C ``` Or to process all this queries at once: ``` $ curl -s 'wttr.in/{Nuremberg,Hamburg,Berlin}?format=3' Nuremberg: 🌦 +11⁰C Hamburg: 🌦 +8⁰C Berlin: 🌦 +8⁰C ``` To specify your own custom output format, use the special `%`-notation: ``` c Weather condition, C Weather condition textual name, x Weather condition, plain-text symbol, h Humidity, t Temperature (Actual), f Temperature (Feels Like), w Wind, l Location, m Moon phase 🌑🌒🌓🌔🌕🌖🌗🌘, M Moon day, p Precipitation (mm/3 hours), P Pressure (hPa), e Dew point, u UV index (1-12), D Dawn*, S Sunrise*, z Zenith*, s Sunset*, d Dusk*, T Current time*, Z Local timezone. (*times are shown in the local timezone) ``` So, these two calls are the same: ``` $ curl wttr.in/London?format=3 London: ⛅️ +7⁰C $ curl wttr.in/London?format="%l:+%c+%t\n" London: ⛅️ +7⁰C ``` ## Integrations Thanks to the ease of integrating *wttr.in* into any program, there are a plethora of popular integrations across various libraries, programming languages, and systems. *wttr.in* is compatible with: * terminal managers, * window managers, * editors, * chat clients, and more, these integrations enhance workflow efficiency by embedding weather information directly into user interfaces. See the full list of integrations here: [wttr.in integrations](doc/integrations.md) and some of them below. ### tmux When using in `tmux.conf`, you have to escape `%` with `%`, i.e. write there `%%` instead of `%`. The output does not contain new line by default, when the %-notation is used, but it does contain it when preconfigured format (`1`,`2`,`3` etc.) are used. To have the new line in the output when the %-notation is used, use '\n' and single quotes when doing a query from the shell. In programs, that are querying the service automatically (such as tmux), it is better to use some reasonable update interval. In tmux, you can configure it with `status-interval`. If several, `:` separated locations, are specified in the query, specify update period as an additional query parameter `period=`: ``` set -g status-interval 60 WEATHER='#(curl -s wttr.in/London:Stockholm:Moscow\?format\="%%l:+%%c%%20%%t%%60%%w&period=60")' set -g status-right "$WEATHER ..." ``` ![wttr.in in tmux status bar](https://wttr.in/files/example-tmux-status-line.png) ### WeeChat To embed in to an IRC ([WeeChat](https://github.com/weechat/weechat)) client's existing status bar: ``` /alias add wttr /exec -pipe "/mute /set plugins.var.wttr" url:wttr.in/Montreal?format=%l:+%c+%f+%h+%p+%P+%m+%w+%S+%s;/wait 3 /item refresh wttr /trigger add wttr timer 60000;0;0 "" "" "/wttr" /item add wttr "" "${plugins.var.wttr}" /eval /set weechat.bar.status.items ${weechat.bar.status.items},spacer,wttr /eval /set weechat.startup.command_after_plugins ${weechat.startup.command_after_plugins};/wttr /wttr ``` ![wttr.in in WeeChat status bar](https://i.imgur.com/XkYiRU7.png) ### conky Conky usage example: ``` ${texeci 1800 curl wttr.in/kyiv_0pq_lang=uk.png | convert - -transparent black $HOME/.config/conky/out.png} ${image $HOME/.config/conky/out.png -p 0,0} ``` ![wttr.in in conky](https://user-images.githubusercontent.com/3875145/172178453-9e9ed9e3-9815-426a-9a21-afdd6e279fc8.png) ### IRC IRC integration example: * https://github.com/OpenSourceTreasure/Mirc-ASCII-weather-translate-pixel-editor ### Emojis support To see emojis in terminal, you need: 1. Terminal support for emojis (was added to Cairo 1.15.8); 2. Font with emojis support. For the emoji font, we recommend *Noto Color Emoji*, and a good alternative option would be the *Emoji One* font; both of them support all necessary emoji glyphs. Font configuration: ```xml $ cat ~/.config/fontconfig/fonts.conf serif Noto Color Emoji sans-serif Noto Color Emoji monospace Noto Color Emoji ``` (to apply the configuration, run `fc-cache -f -v`). In some cases, `tmux` and the terminal understanding of some emoji characters may differ, which may cause strange effects similar to that described in #579. ### Squeak To embed into the world main docking bar: ```smalltalk wttr := (UpdatingStringMorph on: [(WebClient httpGet: 'https://wttr.in/?format=%20%20%l:%20%C+%t') content] selector: #value) stepTime: 60000; useStringFormat; yourself. dockingBar := World mainDockingBars first. dockingBar addMorph: wttr after: (dockingBar findA: ClockMorph). ``` ![wttr.in integration in the Squeak world main docking bar](https://github.com/user-attachments/assets/4c2762b0-77ae-41a8-98db-3eb310d073bd) ## Data-rich output format (v2) In the experimental data-rich output format, that is available under the view code `v2`, a lot of additional weather and astronomical information is available: * Temperature, and precipitation changes forecast throughout the days; * Moonphase for today and the next three days; * The current weather condition, temperature, humidity, wind speed and direction, pressure; * Timezone; * Dawn, sunrise, noon, sunset, dusk time for he selected location; * Precise geographical coordinates for the selected location. ``` $ curl v2.wttr.in/München ``` or ``` $ curl wttr.in/München?format=v2 ``` or, if you prefer Nerd Fonts instead of Emoji, `v2d` (day) or `v2n` (night): ``` $ curl v2d.wttr.in/München ``` ![data-rich output format](https://wttr.in/files/example-wttr-v2.png) (The mode is experimental, and it has several limitations currently: * It works only in terminal; * Only English is supported). Currently, you need some tweaks for some terminals, to get the best possible visualization. ### URXVT Depending on your configuration you might be taking all steps, or only a few. URXVT currently doesn't support emoji related fonts, but we can get almost the same effect using *Font-Symbola*. So add to your `.Xresources` file the following line: ``` xft:symbola:size=10:minspace=False ``` You can add it _after_ your preferred font and it will only show up when required. Then, if you see or feel like you're having spacing issues, add this: `URxvt.letterSpace: 0` For some reason URXVT sometimes stops deciding right the word spacing and we need to force it this way. The result, should look like: ![URXVT Emoji line](https://user-images.githubusercontent.com/24360204/63842949-1d36d480-c975-11e9-81dd-998d1329bd8a.png) ## Map view (v3) In the experimental map view, that is available under the view code `v3`, weather information about a geographical region is available: ``` $ curl v3.wttr.in/Bayern.sxl ``` ![v3.wttr.in/Bayern](https://v3.wttr.in/Bayern.png) or directly in browser: * https://v3.wttr.in/Bayern The map view currently supports three formats: * PNG (for browser and messengers); * Sixel (terminal inline images support); * IIP (terminal with iterm2 inline images protocol support). Terminal with inline images protocols support: ⟶ *Detailed article: [Images in terminal](doc/terminal-images.md)* | Terminal | Environment | Images support | Protocol | | --------------------- | --------- | ------------- | --------- | | uxterm | X11 | yes | Sixel | | mlterm | X11 | yes | Sixel | | kitty | X11 | yes | Kitty | | wezterm | X11 | yes | IIP | | Darktile | X11 | yes | Sixel | | Jexer | X11 | yes | Sixel | | GNOME Terminal | X11 | [in-progress](https://gitlab.gnome.org/GNOME/vte/-/issues/253) | Sixel | | alacritty | X11 | [in-progress](https://github.com/alacritty/alacritty/issues/910) | Sixel | | foot | Wayland | yes | Sixel | | DomTerm | Web | yes | Sixel | | Yaft | FB | yes | Sixel | | iTerm2 | Mac OS X| yes | IIP | | mintty | Windows | yes | Sixel | | Windows Terminal | Windows | [in-progress](https://github.com/microsoft/terminal/issues/448) | Sixel | | [RLogin](http://nanno.dip.jp/softlib/man/rlogin/) | Windows | yes | Sixel | | ## Different output formats ### JSON output The JSON format is a feature providing access to *wttr.in* data through an easy-to-parse format, without requiring the user to create a complex script to reinterpret wttr.in's graphical output. To fetch information in JSON format, use the following syntax: $ curl wttr.in/Detroit?format=j1 This will fetch information on the Detroit region in JSON format. The j1 format code is used to allow for the use of other layouts for the JSON output. The result will look something like the following: ```json { "current_condition": [ { "FeelsLikeC": "25", "FeelsLikeF": "76", "cloudcover": "100", "humidity": "76", "observation_time": "04:08 PM", "precipMM": "0.2", "pressure": "1019", "temp_C": "22", "temp_F": "72", "uvIndex": 5, "visibility": "16", "weatherCode": "122", "weatherDesc": [ { "value": "Overcast" } ], "weatherIconUrl": [ { "value": "" } ], "winddir16Point": "NNE", "winddirDegree": "20", "windspeedKmph": "7", "windspeedMiles": "4" } ], ... ``` Most of these values are self-explanatory, aside from `weatherCode`. The `weatherCode` is an enumeration which you can find at either [the WorldWeatherOnline website](https://www.worldweatheronline.com/developer/api/docs/weather-icons.aspx) or [in the wttr.in source code](https://github.com/chubin/wttr.in/blob/master/lib/constants.py). A smaller version `format=j2` without hourly data is also availble. Can work well for microcontrollers with limited memory. ### Prometheus Metrics Output The [Prometheus](https://github.com/prometheus/prometheus) Metrics format is a feature providing access to *wttr.in* data through an easy-to-parse format for monitoring systems, without requiring the user to create a complex script to reinterpret wttr.in's graphical output. To fetch information in Prometheus format, use the following syntax: $ curl wttr.in/Detroit?format=p1 This will fetch information on the Detroit region in Prometheus Metrics format. The `p1` format code is used to allow for the use of other layouts for the Prometheus Metrics output. A possible configuration for Prometheus could look like this: ```yaml - job_name: 'wttr_in_detroit' static_configs: - targets: ['wttr.in'] metrics_path: '/Detroit' params: format: ['p1'] ``` The result will look something like the following: # HELP temperature_feels_like_celsius Feels Like Temperature in Celsius temperature_feels_like_celsius{forecast="current"} 7 # HELP temperature_feels_like_fahrenheit Feels Like Temperature in Fahrenheit temperature_feels_like_fahrenheit{forecast="current"} 45 [truncated] ... ## Moon phases wttr.in can also be used to check the phase of the Moon. This example shows how to see the current Moon phase in the full-output mode: $ curl wttr.in/Moon Get the moon phase for a particular date by adding `@YYYY-MM-DD`: $ curl wttr.in/Moon@2016-12-25 The moon phase information uses [pyphoon](https://github.com/chubin/pyphoon) as its backend. To get the moon phase information in the online mode, use `%m`: $ curl wttr.in/London?format=%m 🌖 Keep in mind that the Unicode representation of moon phases suffers 2 caveats: - With some fonts, the representation `🌘` is ambiguous, for it either seem almost-shadowed or almost-lit, depending on whether your terminal is in light mode or dark mode. Relying on colored fonts like `noto-fonts` works around this problem. - The representation `🌘` is also ambiguous, for it means "last quarter" in northern hemisphere, but "first quarter" in souther hemisphere. It also means nothing in tropical zones. This is a limitation that [Unicode](https://www.unicode.org/L2/L2017/17304-moon-var.pdf) is aware about. But it has not been worked around at `wttr.in` yet. See #247, #364 for the corresponding tracking issues, and [pyphoon#1](https://github.com/chubin/pyphoon/issues/1) for pyphoon. Any help is welcome. ## Internationalization and localization wttr.in supports multilingual locations names that can be specified in any language in the world (it may be surprising, but many locations in the world don't have an English name). The query string should be specified in Unicode (hex-encoded or not). Spaces in the query string must be replaced with `+`: $ curl wttr.in/станция+Восток Weather report: станция Восток Overcast .--. -65 – -47 °C .-( ). ↑ 23 km/h (___.__)__) 15 km 0.0 mm The language used for the output (except the location name) does not depend on the input language and it is either English (by default) or the preferred language of the browser (if the query was issued from a browser) that is specified in the query headers (`Accept-Language`). The language can be set explicitly when using console clients by using command-line options like this: curl -H "Accept-Language: fr" wttr.in http GET wttr.in Accept-Language:ru The preferred language can be forced using the `lang` option: $ curl wttr.in/Berlin?lang=de The third option is to choose the language using the DNS name used in the query: $ curl de.wttr.in/Berlin wttr.in is currently translated into 54 languages, and the number of supported languages is constantly growing. See [/:translation](https://wttr.in/:translation) to learn more about the translation process, to see the list of supported languages and contributors, or to know how you can help to translate wttr.in in your language. ![Queries to wttr.in in various languages](https://pbs.twimg.com/media/C7hShiDXQAES6z1.jpg) ## Installation To install the application: 1. Install external dependencies 2. Install Python dependencies used by the service 3. Configure IP2Location (optional) 4. Get a WorldWeatherOnline API and configure wego 5. Configure wttr.in 6. Configure the HTTP-frontend service ### Install external dependencies wttr.in has the following external dependencies: * [golang](https://golang.org/doc/install), wego dependency * [wego](https://github.com/schachmat/wego), weather client for terminal After you install [golang](https://golang.org/doc/install), install `wego`: ```bash go install github.com/schachmat/wego@latest ``` ### Install Python dependencies Python requirements: * Flask * geoip2 * geopy * requests * gevent If you want to get weather reports as PNG files, you'll also need to install: * PIL * pyte (>=0.6) * necessary fonts You can install most of them using `pip`. Some python package use LLVM, so install it first: ```bash apt-get install llvm-7 llvm-7-dev ``` If `virtualenv` is used: ```bash virtualenv -p python3 ve ve/bin/pip3 install -r requirements.txt ve/bin/python3 bin/srv.py ``` Also, you need to install the geoip2 database. You can use a free database GeoLite2 that can be downloaded from (http://dev.maxmind.com/geoip/geoip2/geolite2/). ### Configure IP2Location (optional) If you want to use the IP2location service for IP-addresses that are not covered by GeoLite2, you have to obtain a API key of that service, and after that save into the `~/.ip2location.key` file: ``` $ echo 'YOUR_IP2LOCATION_KEY' > ~/.ip2location.key ``` If you don't have this file, the service will be silently skipped (it is not a big problem, because the MaxMind database is pretty good). ### Installation with Docker * Install Docker * Build Docker Image * These files should be mounted by the user at runtime: ``` /root/.wegorc /root/.ip2location.key (optional) /app/airports.dat /app/GeoLite2-City.mmdb ``` ### Get a WorldWeatherOnline key and configure wego To get a WorldWeatherOnline API key, you must register here: https://developer.worldweatheronline.com/auth/register After you have a WorldWeatherOnline key, you can save it into the WWO key file: `~/.wwo.key` Also, you have to specify the key in the `wego` configuration: ```json $ cat ~/.wegorc { "APIKey": "00XXXXXXXXXXXXXXXXXXXXXXXXXXX", "City": "London", "Numdays": 3, "Imperial": false, "Lang": "en" } ``` The `City` parameter in `~/.wegorc` is ignored. ### Configure wttr.in Configure the following environment variables that define the path to the local `wttr.in` installation, to the GeoLite database, and to the `wego` installation. For example: ```bash export WTTR_MYDIR="/home/igor/wttr.in" export WTTR_GEOLITE="/home/igor/wttr.in/GeoLite2-City.mmdb" export WTTR_WEGO="/home/igor/go/bin/wego" export WTTR_LISTEN_HOST="0.0.0.0" export WTTR_LISTEN_PORT="8002" ``` ### Configure the HTTP-frontend service It's recommended that you also configure the web server that will be used to access the service: ```nginx server { listen [::]:80; server_name wttr.in *.wttr.in; access_log /var/log/nginx/wttr.in-access.log main; error_log /var/log/nginx/wttr.in-error.log; location / { proxy_pass http://127.0.0.1:8002; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; client_max_body_size 10m; client_body_buffer_size 128k; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; expires off; } } ``` ## wttr.in usage stats As of March 2026, *wttr.in* handles 22-27 million queries per day from 300,000 to 320,000 users, according to the access logs. ![wttr.in usage stats](share/stats/stats.png) ================================================ FILE: bin/geo-proxy.py ================================================ import gevent from gevent.pywsgi import WSGIServer from gevent.queue import Queue from gevent.monkey import patch_all from gevent.subprocess import Popen, PIPE, STDOUT patch_all() import sys import os import json from flask import ( Flask, request, render_template, send_from_directory, send_file, make_response, jsonify, Response, ) app = Flask(__name__) MYDIR = os.path.abspath(os.path.dirname("__file__")) sys.path.append(os.path.join(MYDIR, "lib")) CACHEDIR = os.path.join(MYDIR, "cache") from geopy.geocoders import Nominatim # , Mapzen # geomapzen = Mapzen("mapzen-RBNbmcZ") # Nominatim() geoosm = Nominatim(timeout=7, user_agent="wttrin-geo/0.0.2") import airports # from tzwhere import tzwhere import timezonefinder tf = timezonefinder.TimezoneFinder() def load_cache(location_string): try: location_string = location_string.replace("/", "_") cachefile = os.path.join(CACHEDIR, location_string) return json.loads(open(cachefile, "r").read()) except Exception: return None def shorten_full_address(address): parts = address.split(",") if len(parts) > 6: parts = parts[:2] + [x for x in parts[-4:] if len(x) < 20] return ",".join(parts) return address def save_cache(location_string, answer): location_string = location_string.replace("/", "_") cachefile = os.path.join(CACHEDIR, location_string) open(cachefile, "w").write(json.dumps(answer)) def query_osm(location_string): try: location = geoosm.geocode(location_string) return { "address": location.address, "latitude": location.latitude, "longitude": location.longitude, } except Exception as e: print(e) return None def add_timezone_information(geo_data): # tzwhere_ = tzwhere.tzwhere() # timezone_str = tzwhere_.tzNameAt(geo_data["latitude"], geo_data["longitude"]) timezone_str = tf.certain_timezone_at( lat=geo_data["latitude"], lng=geo_data["longitude"] ) answer = geo_data.copy() answer["timezone"] = timezone_str return answer @app.route("/") def find_location(location): airport_gps_location = airports.get_airport_gps_location( location.upper().lstrip("~") ) is_airport = False if airport_gps_location is not None: location = airport_gps_location is_airport = True location = location.replace("+", " ") answer = load_cache(location) loaded_answer = None if answer is not None: loaded_answer = answer.copy() print("cache found: %s" % location) if answer is None: answer = query_osm(location) if is_airport: answer["address"] = shorten_full_address(answer["address"]) if "timezone" not in answer: answer = add_timezone_information(answer) if answer is not None and loaded_answer != answer: save_cache(location, answer) if answer is None: return "" else: r = Response(json.dumps(answer)) r.headers["Content-Type"] = "text/json; charset=utf-8" return r app.config["JSON_AS_ASCII"] = False server = WSGIServer(("127.0.0.1", 8004), app) server.serve_forever() ================================================ FILE: bin/proxy.py ================================================ # vim: fileencoding=utf-8 """ The proxy server acts as a backend for the wttr.in service. It caches the answers and handles various data sources transforming their answers into format supported by the wttr.in service. If WTTRIN_TEST is specified, it works in a special test mode: it does not fetch and does not store the data in the cache, but is using the fake data from "test/proxy-data". """ from __future__ import print_function from gevent.pywsgi import WSGIServer from gevent.monkey import patch_all patch_all() # pylint: disable=wrong-import-position,wrong-import-order import sys import os import time import json import hashlib import re import logging import requests import cyrtranslit from flask import Flask, request APP = Flask(__name__) MYDIR = os.path.abspath(os.path.dirname(os.path.dirname("__file__"))) sys.path.append("%s/lib/" % MYDIR) import proxy_log import globals from globals import ( PROXY_CACHEDIR, PROXY_PORT, USE_METNO, USER_AGENT, MISSING_TRANSLATION_LOG, ) from metno import create_standard_json_from_metno, metno_request from translations import PROXY_LANGS # pylint: enable=wrong-import-position proxy_logger = proxy_log.LoggerWWO(globals.PROXY_LOG_ACCESS, globals.PROXY_LOG_ERRORS) def is_testmode(): """Server is running in the wttr.in test mode""" return "WTTRIN_TEST" in os.environ def load_translations(): """ load all translations """ translations = {} for f_name in PROXY_LANGS: f_name = "share/translations/%s.txt" % f_name translation = {} lang = f_name.split("/")[-1].split(".", 1)[0] with open(f_name, "r") as f_file: for line in f_file: if ":" not in line: continue if line.count(":") == 3: _, trans, orig, _ = line.strip().split(":", 4) else: _, trans, orig = line.strip().split(":", 3) trans = trans.strip() orig = orig.strip() translation[orig.lower()] = trans translations[lang] = translation return translations TRANSLATIONS = load_translations() def _is_metno(): return USE_METNO def _find_srv_for_query(path, query): # pylint: disable=unused-argument if _is_metno(): return "https://api.met.no" return "http://api.worldweatheronline.com" def _cache_file(path, query): """Return cache file name for specified `path` and `query` and for the current time. Do smooth load on the server, expiration time is slightly varied basing on the path+query sha1 hash digest. """ digest = hashlib.sha1(("%s %s" % (path, query)).encode("utf-8")).hexdigest() digest_number = ord(digest[0].upper()) penalty = 0 expiry_interval = 60 * (digest_number + penalty) timestamp = "%010d" % (int(time.time()) // expiry_interval * expiry_interval) filename = os.path.join(PROXY_CACHEDIR, timestamp, path, query) return filename def _load_content_and_headers(path, query): if is_testmode(): cache_file = "test/proxy-data/data1" else: cache_file = _cache_file(path, query) try: return ( open(cache_file, "r").read(), json.loads(open(cache_file + ".headers", "r").read()), ) except IOError: return None, None def _touch_empty_file(path, query): cache_file = _cache_file(path, query) cache_dir = os.path.dirname(cache_file) if not os.path.exists(cache_dir): os.makedirs(cache_dir) open(cache_file, "w").write("") def _save_content_and_headers(path, query, content, headers): cache_file = _cache_file(path, query) cache_dir = os.path.dirname(cache_file) if not os.path.exists(cache_dir): os.makedirs(cache_dir) open(cache_file + ".headers", "w").write(json.dumps(headers)) open(cache_file, "wb").write(content) def translate(text, lang): """ Translate `text` into `lang`. If `text` is comma-separated, translate each term independently. If no translation found, leave it untouched. """ def _log_unknown_translation(lang, text): with open(MISSING_TRANSLATION_LOG % lang, "a") as f_missing_translation: f_missing_translation.write(text + "\n") if "," in text: terms = text.split(",") translated_terms = [translate(term.strip(), lang) for term in terms] return ", ".join(translated_terms) if lang not in TRANSLATIONS: _log_unknown_translation(lang, "UNKNOWN_LANGUAGE") return text if text.lower() not in TRANSLATIONS.get(lang, {}): _log_unknown_translation(lang, text) return text translated = TRANSLATIONS.get(lang, {}).get(text.lower(), text) return translated def cyr(to_translate): """ Transliterate `to_translate` from latin into cyrillic """ return cyrtranslit.to_cyrillic(to_translate) def _patch_greek(original): return original.replace("Ηλιόλουστη/ο", "Ηλιόλουστη") def add_translations(content, lang): """ Add `lang` translation to `content` (JSON) returned by the data source """ if content == "{}": return {} languages_to_translate = TRANSLATIONS.keys() try: d = json.loads(content) # pylint: disable=invalid-name except (ValueError, TypeError) as exception: print("---") print(exception) print("---") return {} if "current_condition" not in d["data"]: return content try: weather_condition = ( d["data"]["current_condition"][0]["weatherDesc"][0]["value"] .capitalize() .strip() ) d["data"]["current_condition"][0]["weatherDesc"][0]["value"] = weather_condition if lang in languages_to_translate: d["data"]["current_condition"][0]["lang_%s" % lang] = [ {"value": translate(weather_condition, lang)} ] elif lang == "sr": d["data"]["current_condition"][0]["lang_%s" % lang] = [ { "value": cyr( d["data"]["current_condition"][0]["lang_%s" % lang][0]["value"] ) } ] elif lang == "el": d["data"]["current_condition"][0]["lang_%s" % lang] = [ { "value": _patch_greek( d["data"]["current_condition"][0]["lang_%s" % lang][0]["value"] ) } ] elif lang == "sr-lat": d["data"]["current_condition"][0]["lang_%s" % lang] = [ {"value": d["data"]["current_condition"][0]["lang_sr"][0]["value"]} ] fixed_weather = [] for w in d["data"]["weather"]: # pylint: disable=invalid-name fixed_hourly = [] for h in w["hourly"]: # pylint: disable=invalid-name weather_condition = h["weatherDesc"][0]["value"].strip() if lang in languages_to_translate: h["lang_%s" % lang] = [ {"value": translate(weather_condition, lang)} ] elif lang == "sr": h["lang_%s" % lang] = [ {"value": cyr(h["lang_%s" % lang][0]["value"])} ] elif lang == "el": h["lang_%s" % lang] = [ {"value": _patch_greek(h["lang_%s" % lang][0]["value"])} ] elif lang == "sr-lat": h["lang_%s" % lang] = [{"value": h["lang_sr"][0]["value"]}] fixed_hourly.append(h) w["hourly"] = fixed_hourly fixed_weather.append(w) d["data"]["weather"] = fixed_weather content = json.dumps(d) except (IndexError, ValueError) as exception: print(exception) return content def _fetch_content_and_headers(path, query_string, **kwargs): content, headers = _load_content_and_headers(path, query_string) if content is None: srv = _find_srv_for_query(path, query_string) url = "%s/%s?%s" % (srv, path, query_string) attempts = 10 response = None error = "" while attempts: try: response = requests.get(url, timeout=2, **kwargs) except requests.ReadTimeout: attempts -= 1 continue try: data = json.loads(response.content) error = data.get("data", {}).get("error", "") if error: try: error = error[0]["msg"] except (ValueError, IndexError): error = "invalid error format: %s" % error break except ValueError: attempts -= 1 error = "invalid response" proxy_logger.log(query_string, error) _touch_empty_file(path, query_string) if response: headers = {} headers["Content-Type"] = response.headers["content-type"] _save_content_and_headers(path, query_string, response.content, headers) content = response.content else: content = "{}" return content, headers def _make_query(path, query_string): if _is_metno(): path, query, days = metno_request(path, query_string) if USER_AGENT == "": raise ValueError( "User agent must be set to adhere to metno ToS: https://api.met.no/doc/TermsOfService" ) content, headers = _fetch_content_and_headers( path, query, headers={"User-Agent": USER_AGENT} ) content = create_standard_json_from_metno(content, days) else: # WWO tweaks query_string += "&extra=localObsTime" query_string += "&includelocation=yes" content, headers = _fetch_content_and_headers(path, query_string) return content, headers def _normalize_query_string(query_string): # Normalized query string has the following fixes: # 1. Uses , for the coordinates separation # 2. Limits number of digits after . coord_match = re.search(r"q=[^&]*", query_string) coords_str = coord_match.group(0) coords = re.findall(r"[-0-9.]+", coords_str) if len(coords) != 2: return query_string lat = str(round(float(coords[0]), 2)) lng = str(round(float(coords[1]), 2)) query_string = re.sub(r"q=[^&]*", "q=" + lat + "," + lng, query_string) # print(query_string) # nqs = query_string.replace("%2C", ",") # lat, lng = nqs.split(",", 1) # nqs = f"{lat:.2f},{lng:.2f}" return query_string @APP.route("/") def proxy(path): """ Main proxy function. Handles incoming HTTP queries. """ lang = request.args.get("lang", "en") query_string = request.query_string.decode("utf-8") query_string = _normalize_query_string(query_string) query_string = query_string.replace("sr-lat", "sr") query_string = query_string.replace("lang=None", "lang=en") content = "" headers = "" content, headers = _make_query(path, query_string) # _log_query(path, query_string, error) content = add_translations(content, lang) if "Unable to find any" in content: print(query_string) return content, 200, headers if __name__ == "__main__": # app.run(host='0.0.0.0', port=5001, debug=False) # app.debug = True if len(sys.argv) == 1: bind_addr = "0.0.0.0" logging.getLogger('werkzeug').setLevel(logging.ERROR) # Suppress Werkzeug logs SERVER = WSGIServer((bind_addr, PROXY_PORT), APP, log=None) SERVER.serve_forever() else: print("running single request from command line arg") APP.testing = True with APP.test_client() as c: resp = c.get(sys.argv[1]) print("Status: " + resp.status) # print('Headers: ' + dumps(resp.headers)) print(resp.data.decode("utf-8")) ================================================ FILE: bin/srv.py ================================================ #!/usr/bin/env python # vim: set encoding=utf-8 from gevent.pywsgi import WSGIServer from gevent.monkey import patch_all patch_all() # pylint: disable=wrong-import-position,wrong-import-order import sys import os import jinja2 from flask import Flask, request, send_from_directory, send_file APP = Flask(__name__) MYDIR = os.path.abspath(os.path.dirname(os.path.dirname("__file__"))) sys.path.append("%s/lib/" % MYDIR) import wttr_srv from globals import TEMPLATES, STATIC, LISTEN_HOST, LISTEN_PORT # pylint: enable=wrong-import-position,wrong-import-order # from view.v3 import v3_file MY_LOADER = jinja2.ChoiceLoader( [ APP.jinja_loader, jinja2.FileSystemLoader(TEMPLATES), ] ) APP.jinja_loader = MY_LOADER # @APP.route("/v3/") # def send_v3(location): # filepath = v3_file(location) # if filepath.startswith("ERROR"): # return filepath.rstrip("\n") + "\n" # return send_file(filepath) @APP.route("/files/") def send_static(path): "Send any static file located in /files/" return send_from_directory(STATIC, path) @APP.route("/favicon.ico") def send_favicon(): "Send static file favicon.ico" return send_from_directory(STATIC, "favicon.ico") @APP.route("/malformed-response.html") def send_malformed(): "Send static file malformed-response.html" return send_from_directory(STATIC, "malformed-response.html") @APP.route("/") @APP.route("/") def wttr(location=None): "Main function wrapper" return wttr_srv.wttr(location, request) SERVER = WSGIServer( (LISTEN_HOST, int(os.environ.get("WTTRIN_SRV_PORT", LISTEN_PORT))), APP ) SERVER.serve_forever() ================================================ FILE: config/services/services.yaml ================================================ services: - name: "main server" command: "while true; do sudo /wttr.in/bin/srv big-cache.yaml ; sleep 5; done" workdir: "$HOME" port: ... - name: "geo server" command: "while true; do /wttr.in/bin/srv geo.yaml; sleep 5; done" workdir: "$HOME" env: - NOMINATIM_OPENCAGE port: 8085 - name: "proxy" command: ve/bin/python3 bin/proxy.py workdir: "/wttr.in/wttr.in-v2-v2" port: 5001 - name: "format=j1" command: "WTTRIN_SRV_PORT=9003 ve/bin/python3 bin/srv.py" workdir: "/wttr.in/wttr.in-v2-v2" port: 9003 - name: "format=line" command: "WTTRIN_SRV_PORT=9004 ve/bin/python3 bin/srv.py" workdir: "/wttr.in/wttr.in-v2-v2" port: 9004 - name: "format=v1" command: "WTTRIN_SRV_PORT=9005 ve/bin/python3 bin/srv.py" workdir: "/wttr.in/wttr.in-v2-v2" port: 9005 - name: "filetype=png" command: "WTTRIN_SRV_PORT=9006 ve/bin/python3 bin/srv.py" workdir: "/wttr.in/wttr.in-v2-v2" port: 9006 ================================================ FILE: doc/integrations.md ================================================ ## Integrations Thanks to the ease of integrating *wttr.in* into any program, there are a plethora of popular integrations across various libraries, programming languages, and systems. *wttr.in* is compatible with: * terminal managers, * window managers, * editors, * chat clients, and more, these integrations enhance workflow efficiency by embedding weather information directly into user interfaces. Below, we've compiled a list of some of these integrations. While not exhaustive, it serves as a guide to help you use *wttr.in* within your favorite application or add integration to a new one. | Integration For | Short Description | Repository | |----------------------------------------------------|------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| | **Terminal Managers** | | | | [tmux](#tmux) | Allows embedding weather data in the tmux status bar with customizable update intervals. | - | | **Window Managers Statusbars** | | | | [Waybar](#Waybar) | A custom module in Rust for displaying weather in Waybar, with support for custom indicators and error handling. | [wttrbar](https://github.com/bjesus/wttrbar) | | [Xmobar](#Xmobar) | A Python script for integrating weather data into Xmobar. | [weather-xmobar-wttr.in](https://github.com/alexeygumirov/weather-xmobar-wttr.in) | | [AwesomeWM](#AwesomeWM) | Displays weather in AwesomeWM with a focus on clean UI and tooltips. | [wttr-widget](https://github.com/pivaldi/wttr-widget) | | **Editors** | | | | [Emacs](#Emacs) | An Emacs frontend for showing weather forecasts directly within Emacs. | [emacs-wttrin](https://github.com/cjennings/emacs-wttrin) | | [Emacs](#Emacs) | Displays weather in the Emacs mode line with emoji support, configurable for updates. | [display-wttr](https://git.sr.ht/~josegpt/display-wttr) | | **Chats** | | | | [Conky](#Conky) | Example script for displaying weather in Conky using transparent images. | - | | [WeeChat](#WeeChat) | Script for embedding weather in the WeeChat IRC client's status bar. | - | | [IRC](#IRC) | Uses Qt-based mirc bot to show weather updates. | [IRC integration](https://github.com/OpenSourceTreasure/Mirc-ASCII-weather-translate-pixel-editor) | | **Other** | | | | [ObsidianMD](#ObsidianMD) | Script for embedding weather in Squeak's world main docking bar. | - | | [RainMeter](#Rainmeter) | | - | | [Squeak](#Squeak) | | - | | [Twitch](#Twitch) | | - | | [GoogleSheets/Excel365](#google-sheets--excel-365) | Direct wttr.in access from Google Sheets / Excel 365 | - | | [Raycast](#Raycast) | Weather extension for Raycast | - | ## Terminal Managers ### tmux When using in `tmux.conf`, you have to escape `%` with `%`, i.e. write there `%%` instead of `%`. The output does not contain new line by default, when the %-notation is used, but it does contain it when preconfigured format (`1`,`2`,`3` etc.) are used. To have the new line in the output when the %-notation is used, use '\n' and single quotes when doing a query from the shell. In programs, that are querying the service automatically (such as tmux), it is better to use some reasonable update interval. In tmux, you can configure it with `status-interval`. If several, `:` separated locations, are specified in the query, specify update period as an additional query parameter `period=`: ``` set -g status-interval 60 WEATHER='#(curl -s wttr.in/London:Stockholm:Moscow\?format\="%%l:+%%c%%20%%t%%60%%w&period=60")' set -g status-right "$WEATHER ..." ``` ![wttr.in in tmux status bar](https://wttr.in/files/example-tmux-status-line.png) ## Window Managers Statusbars ### Waybar [wttrbar](https://github.com/bjesus/wttrbar) by *bjesus* A custom module for displaying weather in Waybar using wttr.in. It’s written in Rust for reliability and supports features like custom indicators (e.g., temperature in Celsius or Fahrenheit), location specification, and date formatting. Example configuration: ```json "custom/weather": { "format": "{}°", "tooltip": true, "interval": 3600, "exec": "wttrbar --location=Paris --main-indicator=FeelsLikeC --date-format=%d.%m.%Y", "return-type": "json" } ``` Installation: Compile using cargo build --release or download pre-built binaries. Requires a font supporting emojis (e.g., Noto Emoji) for weather icons. Features: Supports custom styling based on weather conditions (e.g., sunny), handles wttr.in errors gracefully, and allows wind direction display with Unicode arrows. wttrbar ### Xmobar [weather-xmobar-wttr.in](https://github.com/alexeygumirov/weather-xmobar-wttr.in) by *alexeygumirov* A Python-based script for displaying weather status in Xmobar, leveraging wttr.in. It provides a lightweight solution for integrating weather data into the Xmobar status bar. ![wttr-in-Xmodbar](https://raw.githubusercontent.com/alexeygumirov/weather-xmobar-wttr.in/refs/heads/main/screenshot/screenshot_day.png) (displays weather conditions in a compact format suitable for Xmobar). ### AwesomeWM [wttr-widget](https://github.com/pivaldi/wttr-widget) by *pivaldi* A weather widget for AwesomeWM that uses wttr.in to display weather information. It’s designed to integrate seamlessly with the Awesome window manager, providing a tooltip with detailed weather data. Displays weather conditions with a focus on a clean, customizable UI. The screenshot shows a tooltip with weather details like temperature and conditions. ![wttr-in-awesome](https://raw.githubusercontent.com/pivaldi/wttr-widget/refs/heads/master/screenshots/tooltip1.png) (shows a tooltip with weather data integrated into AwesomeWM) ## Editors ### Emacs #### emacs-wttrin by bcbcarl [emacs-wttrin](https://github.com/bcbcarl/emacs-wttrin) by *bcbcarl* An Emacs frontend for wttr.in, allowing users to view weather forecasts directly within Emacs. It’s designed for simplicity and integration into the Emacs workflow. Users can configure it to fetch weather for specific locations and display it in a buffer. #### display-wttr by josegpt [display-wttr](https://git.sr.ht/~josegpt/display-wttr) by *josegpt* Displays wttr.in weather data in the Emacs mode line with emoji support (requires Emacs 28 or later). It’s lightweight and configurable for periodic updates. Configuration Example: ```elisp (use-package display-wttr :config (display-wttr-mode)) ``` Supports custom locations, update intervals, and emoji-based weather display. The repository has moved to https://git.sr.ht/~josegpt/display-wttr. Screenshot: ![display-wttr](https://raw.githubusercontent.com/josegpt/display-wttr/main/emoji-display-wttr.png) (shows weather with emojis in the Emacs mode line). ## Chats ### conky Conky usage example: ``` ${texeci 1800 curl wttr.in/kyiv_0pq_lang=uk.png | convert - -transparent black $HOME/.config/conky/out.png} ${image $HOME/.config/conky/out.png -p 0,0} ``` ![wttr.in in conky](https://user-images.githubusercontent.com/3875145/172178453-9e9ed9e3-9815-426a-9a21-afdd6e279fc8.png) ### WeeChat To embed in to an IRC ([WeeChat](https://github.com/weechat/weechat)) client's existing status bar: ``` /alias add wttr /exec -pipe "/mute /set plugins.var.wttr" url:wttr.in/Montreal?format=%l:+%c+%f+%h+%p+%P+%m+%w+%S+%s;/wait 3 /item refresh wttr /trigger add wttr timer 60000;0;0 "" "" "/wttr" /item add wttr "" "${plugins.var.wttr}" /eval /set weechat.bar.status.items ${weechat.bar.status.items},spacer,wttr /eval /set weechat.startup.command_after_plugins ${weechat.startup.command_after_plugins};/wttr /wttr ``` ![wttr.in in WeeChat status bar](https://i.imgur.com/XkYiRU7.png) ### IRC [IRC integration](https://github.com/OpenSourceTreasure/Mirc-ASCII-weather-translate-pixel-editor) ![wttr-in-irc](https://raw.githubusercontent.com/OpenSourceTreasure/Mirc-ASCII-weather-translate-pixel-editor/main/mirc-bot.png) ## Other ### ObsidianMD Integration A script for ObsidianMD (a note-taking app) integrates wttr.in to embed weather data in daily notes using the Templater plugin. It fetches and displays weather details for a specified location only for the current day’s note. *Usage:* Add a script to the Templater plugin to check if the note’s date matches today and fetch weather data. Example script: ```javascript <%* if (tp.file.title === tp.date.now()) { %> <%* const weather = await requestUrl('https://wttr.in/Berlin?format=%l:+%c+%C+%t+feels+like+%f\nTime:+++++%T\nSunrise:++%S\nSunset:+++%s\nMoon:+++++%m\nWind:+++++%w\nRainfall:+%p\nHumidity:+%h') %> <%* tR += weather.text %> <%* } else { %> Update the weather report! <%* } %> ``` This displays weather details like temperature, sunrise/sunset, moon phase, wind, rainfall, and humidity in the note body. *Features:* Updates only for the current day’s note, supports custom formats, and can be triggered manually via a hotkey. ### Rainmeter Rainmeter, a desktop customization tool for Windows, can use wttr.in to display weather data by parsing its JSON output. This is particularly useful for creating custom desktop widgets. Use a URL like `https://wttr.in/Alexandria,Virginia?format=j1` to fetch JSON data, which includes detailed weather metrics (e.g., temperature, humidity, wind speed, weather code). A Lua script can map weather codes to local icons for visualization. Parses JSON for detailed weather data, supports integration with custom icons (referencing WorldWeatherOnline’s weather codes), and allows for flexible widget design. ### Squeak To embed into the world main docking bar: ```smalltalk wttr := (UpdatingStringMorph on: [(WebClient httpGet: 'https://wttr.in/?format=%20%20%l:%20%C+%t') content] selector: #value) stepTime: 60000; useStringFormat; yourself. dockingBar := World mainDockingBars first. dockingBar addMorph: wttr after: (dockingBar findA: ClockMorph). ``` ![wttr.in integration in the Squeak world main docking bar](https://github.com/user-attachments/assets/4c2762b0-77ae-41a8-98db-3eb310d073bd) ### Twitch wttr.in is used to create a custom `!weather` command for Twitch streams, allowing viewers to query weather for a specific location (e.g., `!weather Toronto`). The command formats wttr.in output for display in chat. ```bash curl "https://wttr.in/Toronto?format=:+%c+%l+is+currently+%C+with+a+temperature+of+%t+(+%f).+The+wind+is+blowing+from+%w.+The+Humidity+is+currently+%h&u" ``` This outputs, e.g., : ⛅️ Toronto is currently Partly Cloudy with a temperature of +7°C (+45°F). The wind is blowing from NE. The humidity is currently 65%. To display Fahrenheit in parentheses, add `?u` to the URL for USCS units. Features: Customizable output for Twitch chat, supports both Celsius and Fahrenheit, and handles location-based queries dynamically. Details: [wttr.in-on-twitch](https://www.reddit.com/r/commandline/comments/1eqoa0w/creating_a_weather_command_using_wttrin_service/) ### Google Sheets / Excel 365 It is possible to show the live weather in Google Sheets. Assume you want the weather image for a specific location, and the location is either hardcoded or stored in a cell. Example 1: Hardcoded Location (London): In a cell (e.g., A1), enter: excel ``` =IMAGE("https://wttr.in/London_0pq.png", 1) ``` This inserts a weather image for London, resized to fit the cell while maintaining the aspect ratio. Example 2: Dynamic Location (from a cell): If cell B1 contains the location (e.g., London), use: ```excel =IMAGE("https://wttr.in/"&B1&"_0pq.png", 4, 100, 200) ``` This inserts the image with a custom size (100px height, 200px width). Adjust height and width as needed. Customize the wttr.in URL (Optional): * `_0pq`: Simple weather image (current conditions, no background). * `_m`: Metric units (e.g., Celsius). * `_u`: USCS units (e.g., Fahrenheit). * `_t`: Transparent background. Example with metric units and transparency: ``` =IMAGE("https://wttr.in/"&B1&"_0pqt_m.png", 1) ``` ![Google Sheets Example](google-sheets.png) ### Raycast * Raycast Store: https://www.raycast.com/tonka3000/weather * Source code: [Github](https://github.com/raycast/extensions/tree/542ed079c2eb5a95df0835d83ab1f1c2b1970e44/extensions/weather/) * Author: [tonka3000](https://github.com/tonka3000) Raycast is a handy tool for Mac users that helps them work faster and more efficiently. It's popular among developers, designers, and tech enthusiasts who want to streamline their workflows. With Raycast, you can quickly launch apps, search for files, and perform everyday tasks all from one central place, boosting productivity and saving time. With the "Weather" extension, you can quickly check information about the current weather. ![wttr.in in raycast](raycast.png) ================================================ FILE: doc/terminal-images.md ================================================ ## Map view (v3) In the experimental map view, that is available under the view code `v3`, weather information about a geographical region is available: ``` $ curl v3.wttr.in/Bayern.sxl ``` ![v3.wttr.in/Bayern](https://v3.wttr.in/Bayern.png) or directly in browser: * https://v3.wttr.in/Bayern The map view currently supports three formats: * PNG (for browser and messangers); * Sixel (terminal inline images support); * IIP (terminal with iterm2 inline images protocol support). ## Terminal with images support | Terminal | Environment | Images support | Protocol | | --------------------- | --------- | ------------- | --------- | | uxterm | X11 | yes | Sixel | | mlterm | X11 | yes | Sixel | | kitty | X11 | yes | Kitty | | wezterm | X11 | yes | IIP | | Darktile | X11 | yes | Sixel | | Jexer | X11 | yes | Sixel | | GNOME Terminal | X11 | [in-progress](https://gitlab.gnome.org/GNOME/vte/-/issues/253) | Sixel | | alacritty | X11 | [in-progress](https://github.com/alacritty/alacritty/issues/910) | Sixel | | st | X11 | [stixel](https://github.com/vizs/stixel) or [st-sixel](https://github.com/galatolofederico/st-sixel) | Sixel | | Konsole | X11 | yes | Sixel | | DomTerm | Web | yes | Sixel | | Yaft | FB | yes | Sixel | | iTerm2 | Mac OS X| yes | IIP | | mintty | Windows | yes | Sixel | | Windows Terminal | Windows | [in-progress](https://github.com/microsoft/terminal/issues/448) | Sixel | | [RLogin](http://nanno.dip.jp/softlib/man/rlogin/) | Windows | yes | Sixel | | Support in all VTE-based terminals: termite, terminator, etc is more or less the same as in the GNOME Terminal ## Notes ### xterm/uxterm To start xterm/uxterm with Sixel support: ``` uxterm -ti vt340 ``` ### Kitty To view images in kitty: ``` curl -s v3.wttr.in/Tabasco.png | kitty icat --align=left ``` or even without `curl` at all, because `icat` knows how to handle URLs: ``` kitty icat --align=left https://v3.wttr.in/Tabasco.png ``` ================================================ FILE: go.mod ================================================ module github.com/chubin/wttr.in go 1.16 require ( github.com/alecthomas/kong v1.7.0 github.com/denisenkom/go-mssqldb v0.0.0-20200910202707-1e08a3fab204 // indirect github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/hashicorp/golang-lru v1.0.2 github.com/itchyny/gojq v0.12.11 // indirect github.com/klauspost/lctime v0.1.0 github.com/lib/pq v1.8.0 // indirect github.com/mattn/go-colorable v0.1.14 github.com/mattn/go-runewidth v0.0.16 github.com/mattn/go-sqlite3 v1.14.24 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron v1.2.0 github.com/samonzeweb/godb v1.0.13 github.com/sirupsen/logrus v1.9.3 github.com/smartystreets/assertions v1.2.0 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.8.1 // indirect github.com/zsefvlol/timezonemapper v1.0.0 golang.org/x/crypto v0.17.0 // indirect golang.org/x/sys v0.30.0 // indirect google.golang.org/appengine v1.6.3 // indirect gopkg.in/yaml.v3 v3.0.1 ) ================================================ FILE: go.sum ================================================ github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/kong v0.7.1 h1:azoTh0IOfwlAX3qN9sHWTxACE2oV8Bg2gAwBsMwDQY4= github.com/alecthomas/kong v0.7.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= github.com/alecthomas/kong v1.7.0 h1:MnT8+5JxFDCvISeI6vgd/mFbAJwueJ/pqQNzZMsiqZE= github.com/alecthomas/kong v1.7.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.0.0-20190920000552-128d9f4ae1cd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20200910202707-1e08a3fab204/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/itchyny/gojq v0.12.11 h1:YhLueoHhHiN4mkfM+3AyJV6EPcCxKZsOnYf+aVSwaQw= github.com/itchyny/gojq v0.12.11/go.mod h1:o3FT8Gkbg/geT4pLI0tF3hvip5F3Y/uskjRz9OYa38g= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/lctime v0.1.0 h1:nINsuFc860M9cyYhT6vfg6U1USh7kiVBj/s/2b04U70= github.com/klauspost/lctime v0.1.0/go.mod h1:OwdMhr8tbQvusAsnilqkkgDQqivWlqyg0w5cfXkLiDk= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/samonzeweb/godb v1.0.8 h1:WRn6nq0FChYOzh+w8SgpXHUkEhL7W6ZqkCf5Ninx7Uc= github.com/samonzeweb/godb v1.0.8/go.mod h1:LNDt3CakfBwpRY4AD0y/QPTbj+jB6O17tSxQES0p47o= github.com/samonzeweb/godb v1.0.13 h1:dEWijZGizhSN7oOLFYq0+NquG54DCJ9WG55bEcH7GOA= github.com/samonzeweb/godb v1.0.13/go.mod h1:Dcm9f9+aO6bQin4Ce4X3oOM2gvhGMt2naLIDLPQSaWQ= github.com/samonzeweb/godb v1.0.15 h1:HyNb8o1w109as9KWE8ih1YIBe8jC4luJ22f1XNacW38= github.com/samonzeweb/godb v1.0.15/go.mod h1:SxCHqyireDXNrZApknS9lGUEutA43x9eJF632ecbK5Q= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zsefvlol/timezonemapper v1.0.0 h1:HXqkOzf01gXYh2nDQcDSROikFgMaximnhE8BY9SyF6E= github.com/zsefvlol/timezonemapper v1.0.0/go.mod h1:cVUCOLEmc/VvOMusEhpd2G/UBtadL26ZVz2syODXDoQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.3/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: internal/config/config.go ================================================ package config import ( "log" "os" "gopkg.in/yaml.v3" "github.com/chubin/wttr.in/internal/types" "github.com/chubin/wttr.in/internal/util" ) // Config of the program. type Config struct { Cache Geo Logging Server Uplink } // Logging configuration. type Logging struct { // AccessLog path. AccessLog string `yaml:"accessLog,omitempty"` // ErrorsLog path. ErrorsLog string `yaml:"errorsLog,omitempty"` // Interval between access log flushes, in seconds. Interval int `yaml:"interval,omitempty"` } // Server configuration. type Server struct { // PortHTTP is port where HTTP server must listen. // If 0, HTTP is disabled. PortHTTP int `yaml:"portHttp,omitempty"` // PortHTTP is port where the HTTPS server must listen. // If 0, HTTPS is disabled. PortHTTPS int `yaml:"portHttps,omitempty"` // TLSCertFile contains path to cert file for TLS Server. TLSCertFile string `yaml:"tlsCertFile,omitempty"` // TLSCertFile contains path to key file for TLS Server. TLSKeyFile string `yaml:"tlsKeyFile,omitempty"` } // Uplink configuration. type Uplink struct { // Address1 contains address of the uplink server in form IP:PORT // for format=j1 queries. Address1 string `yaml:"address1,omitempty"` // Address2 contains address of the uplink server in form IP:PORT // for format=* queries. Address2 string `yaml:"address2,omitempty"` // Address3 contains address of the uplink server in form IP:PORT // for all other queries. Address3 string `yaml:"address3,omitempty"` // Address4 contains address of the uplink server in form IP:PORT // for PNG queries. Address4 string `yaml:"address4,omitempty"` // Timeout for upstream queries. Timeout int `yaml:"timeout,omitempty"` // PrefetchInterval contains time (in milliseconds) indicating, // how long the prefetch procedure should take. PrefetchInterval int `yaml:"prefetchInterval,omitempty"` } // Cache configuration. type Cache struct { // Size of the main cache. Size int `yaml:"size,omitempty"` } // Geo contains geolocation configuration. type Geo struct { // IPCache contains the path to the IP Geodata cache. IPCache string `yaml:"ipCache,omitempty"` // IPCacheDB contains the path to the SQLite DB with the IP Geodata cache. IPCacheDB string `yaml:"ipCacheDb,omitempty"` IPCacheType types.CacheType `yaml:"ipCacheType,omitempty"` // LocationCache contains the path to the Location Geodata cache. LocationCache string `yaml:"locationCache,omitempty"` // LocationCacheDB contains the path to the SQLite DB with the Location Geodata cache. LocationCacheDB string `yaml:"locationCacheDb,omitempty"` LocationCacheType types.CacheType `yaml:"locationCacheType,omitempty"` Nominatim []Nominatim `yaml:"nominatim"` } type Nominatim struct { Name string // Type describes the type of the location service. // Supported types: iq. Type string URL string Token string } // Default contains the default configuration. func Default() *Config { return &Config{ Cache{ Size: 12800, }, Geo{ IPCache: "/wttr.in/cache/ip2l", IPCacheDB: "/wttr.in/cache/geoip.db", IPCacheType: types.CacheTypeDB, LocationCache: "/wttr.in/cache/loc", LocationCacheDB: "/wttr.in/cache/geoloc.db", LocationCacheType: types.CacheTypeDB, Nominatim: []Nominatim{ { Name: "locationiq", Type: "iq", URL: "https://eu1.locationiq.com/v1/search", Token: os.Getenv("NOMINATIM_LOCATIONIQ"), }, { Name: "opencage", Type: "opencage", URL: "https://api.opencagedata.com/geocode/v1/json", Token: os.Getenv("NOMINATIM_OPENCAGE"), }, }, }, Logging{ AccessLog: "/wttr.in/log/access.log", ErrorsLog: "/wttr.in/log/errors.log", Interval: 300, }, Server{ PortHTTP: 8083, PortHTTPS: 8084, TLSCertFile: "/wttr.in/etc/fullchain.pem", TLSKeyFile: "/wttr.in/etc/privkey.pem", }, Uplink{ Address1: "127.0.0.1:9002", Address2: "127.0.0.1:9002", Address3: "127.0.0.1:9002", Address4: "127.0.0.1:9002", Timeout: 30, PrefetchInterval: 300, }, } } // Load config from file. func Load(filename string) (*Config, error) { var ( config Config data []byte err error ) data, err = os.ReadFile(filename) if err != nil { return nil, err } err = util.YamlUnmarshalStrict(data, &config) if err != nil { return nil, err } return &config, nil } func (c *Config) Dump() []byte { data, err := yaml.Marshal(c) if err != nil { // should never happen. log.Fatalln("config.Dump():", err) } return data } ================================================ FILE: internal/fmt/png/colors.go ================================================ package main // Source: https://www.ditig.com/downloads/256-colors.json var ansiColorsDB = [][3]float64{ { 0, 0, 0, }, { 128, 0, 0, }, { 0, 128, 0, }, { 128, 128, 0, }, { 0, 0, 128, }, { 128, 0, 128, }, { 0, 128, 128, }, { 192, 192, 192, }, { 128, 128, 128, }, { 255, 0, 0, }, { 0, 255, 0, }, { 255, 255, 0, }, { 0, 0, 255, }, { 255, 0, 255, }, { 0, 255, 255, }, { 255, 255, 255, }, { 0, 0, 0, }, { 0, 0, 95, }, { 0, 0, 135, }, { 0, 0, 175, }, { 0, 0, 215, }, { 0, 0, 255, }, { 0, 95, 0, }, { 0, 95, 95, }, { 0, 95, 135, }, { 0, 95, 175, }, { 0, 95, 215, }, { 0, 95, 255, }, { 0, 135, 0, }, { 0, 135, 95, }, { 0, 135, 135, }, { 0, 135, 175, }, { 0, 135, 215, }, { 0, 135, 255, }, { 0, 175, 0, }, { 0, 175, 95, }, { 0, 175, 135, }, { 0, 175, 175, }, { 0, 175, 215, }, { 0, 175, 255, }, { 0, 215, 0, }, { 0, 215, 95, }, { 0, 215, 135, }, { 0, 215, 175, }, { 0, 215, 215, }, { 0, 215, 255, }, { 0, 255, 0, }, { 0, 255, 95, }, { 0, 255, 135, }, { 0, 255, 175, }, { 0, 255, 215, }, { 0, 255, 255, }, { 95, 0, 0, }, { 95, 0, 95, }, { 95, 0, 135, }, { 95, 0, 175, }, { 95, 0, 215, }, { 95, 0, 255, }, { 95, 95, 0, }, { 95, 95, 95, }, { 95, 95, 135, }, { 95, 95, 175, }, { 95, 95, 215, }, { 95, 95, 255, }, { 95, 135, 0, }, { 95, 135, 95, }, { 95, 135, 135, }, { 95, 135, 175, }, { 95, 135, 215, }, { 95, 135, 255, }, { 95, 175, 0, }, { 95, 175, 95, }, { 95, 175, 135, }, { 95, 175, 175, }, { 95, 175, 215, }, { 95, 175, 255, }, { 95, 215, 0, }, { 95, 215, 95, }, { 95, 215, 135, }, { 95, 215, 175, }, { 95, 215, 215, }, { 95, 215, 255, }, { 95, 255, 0, }, { 95, 255, 95, }, { 95, 255, 135, }, { 95, 255, 175, }, { 95, 255, 215, }, { 95, 255, 255, }, { 135, 0, 0, }, { 135, 0, 95, }, { 135, 0, 135, }, { 135, 0, 175, }, { 135, 0, 215, }, { 135, 0, 255, }, { 135, 95, 0, }, { 135, 95, 95, }, { 135, 95, 135, }, { 135, 95, 175, }, { 135, 95, 215, }, { 135, 95, 255, }, { 135, 135, 0, }, { 135, 135, 95, }, { 135, 135, 135, }, { 135, 135, 175, }, { 135, 135, 215, }, { 135, 135, 255, }, { 135, 175, 0, }, { 135, 175, 95, }, { 135, 175, 135, }, { 135, 175, 175, }, { 135, 175, 215, }, { 135, 175, 255, }, { 135, 215, 0, }, { 135, 215, 95, }, { 135, 215, 135, }, { 135, 215, 175, }, { 135, 215, 215, }, { 135, 215, 255, }, { 135, 255, 0, }, { 135, 255, 95, }, { 135, 255, 135, }, { 135, 255, 175, }, { 135, 255, 215, }, { 135, 255, 255, }, { 175, 0, 0, }, { 175, 0, 95, }, { 175, 0, 135, }, { 175, 0, 175, }, { 175, 0, 215, }, { 175, 0, 255, }, { 175, 95, 0, }, { 175, 95, 95, }, { 175, 95, 135, }, { 175, 95, 175, }, { 175, 95, 215, }, { 175, 95, 255, }, { 175, 135, 0, }, { 175, 135, 95, }, { 175, 135, 135, }, { 175, 135, 175, }, { 175, 135, 215, }, { 175, 135, 255, }, { 175, 175, 0, }, { 175, 175, 95, }, { 175, 175, 135, }, { 175, 175, 175, }, { 175, 175, 215, }, { 175, 175, 255, }, { 175, 215, 0, }, { 175, 215, 95, }, { 175, 215, 135, }, { 175, 215, 175, }, { 175, 215, 215, }, { 175, 215, 255, }, { 175, 255, 0, }, { 175, 255, 95, }, { 175, 255, 135, }, { 175, 255, 175, }, { 175, 255, 215, }, { 175, 255, 255, }, { 215, 0, 0, }, { 215, 0, 95, }, { 215, 0, 135, }, { 215, 0, 175, }, { 215, 0, 215, }, { 215, 0, 255, }, { 215, 95, 0, }, { 215, 95, 95, }, { 215, 95, 135, }, { 215, 95, 175, }, { 215, 95, 215, }, { 215, 95, 255, }, { 215, 135, 0, }, { 215, 135, 95, }, { 215, 135, 135, }, { 215, 135, 175, }, { 215, 135, 215, }, { 215, 135, 255, }, { 215, 175, 0, }, { 215, 175, 95, }, { 215, 175, 135, }, { 215, 175, 175, }, { 215, 175, 215, }, { 215, 175, 255, }, { 215, 215, 0, }, { 215, 215, 95, }, { 215, 215, 135, }, { 215, 215, 175, }, { 215, 215, 215, }, { 215, 215, 255, }, { 215, 255, 0, }, { 215, 255, 95, }, { 215, 255, 135, }, { 215, 255, 175, }, { 215, 255, 215, }, { 215, 255, 255, }, { 255, 0, 0, }, { 255, 0, 95, }, { 255, 0, 135, }, { 255, 0, 175, }, { 255, 0, 215, }, { 255, 0, 255, }, { 255, 95, 0, }, { 255, 95, 95, }, { 255, 95, 135, }, { 255, 95, 175, }, { 255, 95, 215, }, { 255, 95, 255, }, { 255, 135, 0, }, { 255, 135, 95, }, { 255, 135, 135, }, { 255, 135, 175, }, { 255, 135, 215, }, { 255, 135, 255, }, { 255, 175, 0, }, { 255, 175, 95, }, { 255, 175, 135, }, { 255, 175, 175, }, { 255, 175, 215, }, { 255, 175, 255, }, { 255, 215, 0, }, { 255, 215, 95, }, { 255, 215, 135, }, { 255, 215, 175, }, { 255, 215, 215, }, { 255, 215, 255, }, { 255, 255, 0, }, { 255, 255, 95, }, { 255, 255, 135, }, { 255, 255, 175, }, { 255, 255, 215, }, { 255, 255, 255, }, { 8, 8, 8, }, { 18, 18, 18, }, { 28, 28, 28, }, { 38, 38, 38, }, { 48, 48, 48, }, { 58, 58, 58, }, { 68, 68, 68, }, { 78, 78, 78, }, { 88, 88, 88, }, { 98, 98, 98, }, { 108, 108, 108, }, { 118, 118, 118, }, { 128, 128, 128, }, { 138, 138, 138, }, { 148, 148, 148, }, { 158, 158, 158, }, { 168, 168, 168, }, { 178, 178, 178, }, { 188, 188, 188, }, { 198, 198, 198, }, { 208, 208, 208, }, { 218, 218, 218, }, { 228, 228, 228, }, { 238, 238, 238, }, } ================================================ FILE: internal/fmt/png/go.mod ================================================ module example.com/m/v2 go 1.20 require ( github.com/chubin/vt10x v0.0.0-20231112153020-ef4f56837bf1 // indirect github.com/fogleman/gg v1.3.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect golang.org/x/image v0.14.0 // indirect ) ================================================ FILE: internal/fmt/png/go.sum ================================================ github.com/chubin/vt10x v0.0.0-20231112153020-ef4f56837bf1 h1:CHg5BTAJZmCjBaAAQrD92s248JHH3JTsLlaC6QBJo/Y= github.com/chubin/vt10x v0.0.0-20231112153020-ef4f56837bf1/go.mod h1:mQssL2gI1LTqWgbffl6DESqe6QkAF67ujBdzSe4bWkU= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= ================================================ FILE: internal/fmt/png/png.go ================================================ package main import ( "fmt" "log" "os" "strings" "github.com/chubin/vt10x" "github.com/fogleman/gg" ) func StringSliceToRuneSlice(s string) [][]rune { strings := strings.Split(s, "\n") result := make([][]rune, len(strings)) i := 0 for _, str := range strings { if len(str) == 0 { continue } result[i] = []rune(str) i++ } return result } func maxRowLength(rows [][]rune) int { maxLen := 0 for _, row := range rows { if len(row) > maxLen { maxLen = len(row) } } return maxLen } func GeneratePng() { runes := StringSliceToRuneSlice(` Weather report: Hochstadt an der Aisch, Germany \ / Partly cloudy _ /"".-. +5(2) °C \_( ). ↗ 9 km/h /(___(__) 10 km 0.0 mm ┌─────────────┐ ┌───────────────────────┤ Sat 11 Nov ├───────────────────────┐ │ Noon └──────┬──────┘ Night │ ├──────────────────────────────┼──────────────────────────────┤ │ _'/"".-. Patchy rain po…│ _'/"".-. Patchy rain po…│ │ ,\_( ). +6(3) °C │ ,\_( ). +5(2) °C │ │ /(___(__) → 22-29 km/h │ /(___(__) ↗ 14-20 km/h │ │ ‘ ‘ ‘ ‘ 10 km │ ‘ ‘ ‘ ‘ 10 km │ │ ‘ ‘ ‘ ‘ 0.1 mm | 86% │ ‘ ‘ ‘ ‘ 0.0 mm | 89% │ └──────────────────────────────┴──────────────────────────────┘ ┌─────────────┐ ┌───────────────────────┤ Sun 12 Nov ├───────────────────────┐ │ Noon └──────┬──────┘ Night │ ├──────────────────────────────┼──────────────────────────────┤ │ \ / Partly cloudy │ .-. Light drizzle │ │ _ /"".-. +8(7) °C │ ( ). +5(2) °C │ │ \_( ). ↑ 7-8 km/h │ (___(__) ↑ 13-18 km/h │ │ /(___(__) 10 km │ ‘ ‘ ‘ ‘ 2 km │ │ 0.0 mm | 0% │ ‘ ‘ ‘ ‘ 0.3 mm | 76% │ └──────────────────────────────┴──────────────────────────────┘ `) // Dimensions of each rune in pixels runeWidth := 8 runeHeight := 14 // Compute the width and height of the final image imageWidth := runeWidth * maxRowLength(runes) imageHeight := runeHeight * len(runes) // Create a new context with the computed dimensions dc := gg.NewContext(imageWidth, imageHeight) // fontPath := "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" // fontPath := "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc" fontPath := "/usr/share/fonts/truetype/lexi/LexiGulim.ttf" err := dc.LoadFontFace(fontPath, 13) if err != nil { log.Fatal(err) } // Loop through each rune in the array and draw it on the context for i, row := range runes { for j, char := range row { // Compute the x and y coordinates for drawing the current rune x := float64(j*runeWidth + runeWidth/2) y := float64(i*runeHeight + runeHeight/2) // Set the appropriate color for the current rune if char == '#' { dc.SetRGB(0, 0, 0) // Black } else if char == '@' { dc.SetRGB(1, 0, 0) // Red } else { dc.SetRGB(1, 1, 1) // White } character := string(char) // if char == ' ' { // character = fmt.Sprint(j % 10) // } dc.DrawRectangle(x, y, x+float64(runeWidth), y+float64(runeHeight)) dc.Fill() // Draw a rectangle with the rune's dimensions and color dc.DrawString(character, x, y) // Draw the character centered on the canvas // dc.DrawStringAnchored(character, x, y, 0.5, 0.5) // Draw the character centered on the canvas } } // Save the image to a PNG file err = dc.SavePNG("output.png") if err != nil { fmt.Println("Error saving PNG:", err) return } fmt.Println("PNG generated successfully") } func GeneratePngFromANSI(input []byte, outputFile string) error { // Dimensions of each rune in pixels runeWidth := 8 runeHeight := 14 fontSize := 13.0 // fontPath := "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" fontPath := "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc" imageCols := 80 imageRows := 25 // Compute the width and height of the final image imageWidth := runeWidth * imageCols imageHeight := runeHeight * imageRows // Create terminal and feed it with input. term := vt10x.New(vt10x.WithSize(imageCols, imageRows)) _, err := term.Write([]byte("\033[20h")) if err != nil { return fmt.Errorf("virtual terminal write error: %w", err) } _, err = term.Write(input) if err != nil { return fmt.Errorf("virtual terminal write error: %w", err) } // Create a new context with the computed dimensions dc := gg.NewContext(imageWidth, imageHeight) err = dc.LoadFontFace(fontPath, fontSize) // Set font size to 96 if err != nil { return fmt.Errorf("error loading font: %w", err) } // Loop through each rune in the array and draw it on the context for i := 0; i < imageRows; i++ { for j := 0; j < imageCols; j++ { // Compute the x and y coordinates for drawing the current rune x := float64(j * runeWidth) y := float64(i * runeHeight) cell := term.Cell(j, i) character := string(cell.Char) dc.DrawRectangle(x, y, float64(runeWidth), float64(runeHeight)) bg := colorANSItoRGB(cell.BG) dc.SetRGB(bg[0], bg[1], bg[2]) dc.Fill() fg := colorANSItoRGB(cell.FG) dc.SetRGB(fg[0], fg[1], fg[2]) // Draw a rectangle with the rune's dimensions and color dc.DrawString(character, x, y+float64(runeHeight)-3) // Draw the character centered on the canvas // dc.DrawStringAnchored(character, x, y, 0.5, 0.5) // Draw the character centered on the canvas } } // Save the image to a PNG file err = dc.SavePNG(outputFile) if err != nil { return fmt.Errorf("error saving png: %w", err) } return nil } func colorANSItoRGB(colorANSI vt10x.Color) [3]float64 { defaultBG := vt10x.Color(0) defaultFG := vt10x.Color(8) if colorANSI == vt10x.DefaultFG { colorANSI = defaultFG } if colorANSI == vt10x.DefaultBG { colorANSI = defaultBG } if colorANSI > 255 { return [3]float64{127, 127, 127} } return ansiColorsDB[colorANSI] } func main() { data, err := os.ReadFile("zh-text.txt") if err != nil { log.Fatalln(err) } err = GeneratePngFromANSI(data, "output.png") if err != nil { log.Fatalln(err) } } ================================================ FILE: internal/geo/ip/convert.go ================================================ package ip import ( "fmt" "log" "path/filepath" "github.com/samonzeweb/godb" "github.com/samonzeweb/godb/adapters/sqlite" "github.com/chubin/wttr.in/internal/util" ) //nolint:cyclop func (c *Cache) ConvertCache() error { dbfile := c.config.Geo.IPCacheDB err := util.RemoveFileIfExists(dbfile) if err != nil { return err } db, err := godb.Open(sqlite.Adapter, dbfile) if err != nil { return err } err = createTable(db, "Address") if err != nil { return err } log.Println("listing cache entries...") files, err := filepath.Glob(filepath.Join(c.config.Geo.IPCache, "*")) if err != nil { return err } log.Printf("going to convert %d entries\n", len(files)) block := []Address{} for i, file := range files { ip := filepath.Base(file) loc, err := c.Read(ip) if err != nil { log.Println("invalid entry for", ip) continue } block = append(block, *loc) if i%1000 != 0 || i == 0 { continue } err = db.BulkInsert(&block).Do() if err != nil { return err } block = []Address{} log.Println("converted", i+1, "entries") } // inserting the rest. err = db.BulkInsert(&block).Do() if err != nil { return err } log.Println("converted", len(files), "entries") return nil } func createTable(db *godb.DB, tableName string) error { createTable := fmt.Sprintf( `create table %s ( name text not null primary key, fullName text not null, lat text not null, long text not null); `, tableName) _, err := db.CurrentDB().Exec(createTable) return err } ================================================ FILE: internal/geo/ip/ip.go ================================================ package ip import ( "fmt" "log" "net/http" "os" "path" "regexp" "strconv" "strings" "github.com/samonzeweb/godb" "github.com/samonzeweb/godb/adapters/sqlite" "github.com/chubin/wttr.in/internal/config" "github.com/chubin/wttr.in/internal/routing" "github.com/chubin/wttr.in/internal/types" "github.com/chubin/wttr.in/internal/util" ) // Address information. type Address struct { IP string `db:"ip,key"` CountryCode string `db:"countryCode"` Country string `db:"country"` Region string `db:"region"` City string `db:"city"` Latitude float64 `db:"latitude"` Longitude float64 `db:"longitude"` } func (l *Address) String() string { if l.Latitude == -1000 { return fmt.Sprintf( "%s;%s;%s;%s", l.CountryCode, l.Country, l.Region, l.City) } return fmt.Sprintf( "%s;%s;%s;%s;%v;%v", l.CountryCode, l.Country, l.Region, l.City, l.Latitude, l.Longitude) } // Cache provides access to the IP Geodata cache. type Cache struct { config *config.Config db *godb.DB } // NewCache returns new cache reader for the specified config. func NewCache(config *config.Config) (*Cache, error) { db, err := godb.Open(sqlite.Adapter, config.Geo.IPCacheDB) if err != nil { return nil, err } // Needed for "upsert" implementation in Put() db.UseErrorParser() return &Cache{ config: config, db: db, }, nil } // Read returns location information from the cache, if found, // or types.ErrNotFound if not found. If the entry is found, but its format // is invalid, types.ErrInvalidCacheEntry is returned. // // Format: // // [CountryCode];Country;Region;City;[Latitude];[Longitude] // // Example: // // DE;Germany;Free and Hanseatic City of Hamburg;Hamburg;53.5736;9.9782 // func (c *Cache) Read(addr string) (*Address, error) { if c.config.Geo.IPCacheType == types.CacheTypeDB { return c.readFromCacheDB(addr) } return c.readFromCacheFile(addr) } func (c *Cache) readFromCacheFile(addr string) (*Address, error) { bytes, err := os.ReadFile(c.cacheFile(addr)) if err != nil { return nil, types.ErrNotFound } return NewAddressFromString(addr, string(bytes)) } func (c *Cache) readFromCacheDB(addr string) (*Address, error) { result := Address{} err := c.db.Select(&result). Where("IP = ?", addr). Do() if err != nil { return nil, err } return &result, nil } func (c *Cache) Put(addr string, loc *Address) error { if c.config.Geo.IPCacheType == types.CacheTypeDB { return c.putToCacheDB(loc) } return c.putToCacheFile(addr, loc) } func (c *Cache) putToCacheDB(loc *Address) error { err := c.db.Insert(loc).Do() // it should work like this: // // target := dberror.UniqueConstraint{} // if errors.As(err, &target) { // // See: https://github.com/samonzeweb/godb/pull/23 // // But for some reason it does not work, // so the dirty hack is used: if strings.Contains(fmt.Sprint(err), "UNIQUE constraint failed") { return c.db.Update(loc).Do() } return err } func (c *Cache) putToCacheFile(addr string, loc fmt.Stringer) error { return os.WriteFile(c.cacheFile(addr), []byte(loc.String()), 0o600) } // cacheFile returns path to the cache entry for addr. func (c *Cache) cacheFile(addr string) string { return path.Join(c.config.Geo.IPCache, addr) } // NewAddressFromString parses the location cache entry s, // and return location, or error, if the cache entry is invalid. func NewAddressFromString(addr, s string) (*Address, error) { var ( lat float64 = -1000 long float64 = -1000 err error ) parts := strings.Split(s, ";") if len(parts) < 4 { return nil, types.ErrInvalidCacheEntry } if len(parts) >= 6 { lat, err = strconv.ParseFloat(parts[4], 64) if err != nil { return nil, types.ErrInvalidCacheEntry } long, err = strconv.ParseFloat(parts[5], 64) if err != nil { return nil, types.ErrInvalidCacheEntry } } return &Address{ IP: addr, CountryCode: parts[0], Country: parts[1], Region: parts[2], City: parts[3], Latitude: lat, Longitude: long, }, nil } // Response provides routing interface to the geo cache. // // Temporary workaround to switch IP addresses handling to the Go server. // Handles two queries: // // - /:geo-ip-put?ip=IP&value=VALUE // - /:geo-ip-get?ip=IP // //nolint:cyclop func (c *Cache) Response(r *http.Request) *routing.Cadre { var ( respERR = &routing.Cadre{Body: []byte("ERR")} respOK = &routing.Cadre{Body: []byte("OK")} ) if ip := util.ReadUserIP(r); ip != "127.0.0.1" { log.Printf("geoIP access from %s rejected\n", ip) return nil } if r.URL.Path == "/:geo-ip-put" { ip := r.URL.Query().Get("ip") value := r.URL.Query().Get("value") if !validIP4(ip) || value == "" { log.Printf("invalid geoIP put query: ip='%s' value='%s'\n", ip, value) return respERR } location, err := NewAddressFromString(ip, value) if err != nil { return respERR } err = c.Put(ip, location) if err != nil { return respERR } return respOK } if r.URL.Path == "/:geo-ip-get" { ip := r.URL.Query().Get("ip") if !validIP4(ip) { return respERR } result, err := c.Read(ip) if result == nil || err != nil { return respERR } return &routing.Cadre{Body: []byte(result.String())} } return nil } func validIP4(ipAddress string) bool { re := regexp.MustCompile( `^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`) return re.MatchString(strings.Trim(ipAddress, " ")) } ================================================ FILE: internal/geo/ip/ip_test.go ================================================ package ip_test import ( "testing" "github.com/stretchr/testify/require" . "github.com/chubin/wttr.in/internal/geo/ip" "github.com/chubin/wttr.in/internal/types" ) //nolint:funlen func TestParseCacheEntry(t *testing.T) { t.Parallel() tests := []struct { addr string input string expected Address err error }{ { "1.2.3.4", "DE;Germany;Free and Hanseatic City of Hamburg;Hamburg;53.5736;9.9782", Address{ IP: "1.2.3.4", CountryCode: "DE", Country: "Germany", Region: "Free and Hanseatic City of Hamburg", City: "Hamburg", Latitude: 53.5736, Longitude: 9.9782, }, nil, }, { "1.2.3.4", "ES;Spain;Madrid, Comunidad de;Madrid;40.4165;-3.70256;28223;Orange Espagne SA;orange.es", Address{ IP: "1.2.3.4", CountryCode: "ES", Country: "Spain", Region: "Madrid, Comunidad de", City: "Madrid", Latitude: 40.4165, Longitude: -3.70256, }, nil, }, { "1.2.3.4", "US;United States of America;California;Mountain View", Address{ IP: "1.2.3.4", CountryCode: "US", Country: "United States of America", Region: "California", City: "Mountain View", Latitude: -1000, Longitude: -1000, }, nil, }, // Invalid entries { "1.2.3.4", "DE;Germany;Free and Hanseatic City of Hamburg;Hamburg;53.5736;XXX", Address{}, types.ErrInvalidCacheEntry, }, } for _, tt := range tests { result, err := NewAddressFromString(tt.addr, tt.input) if tt.err == nil { require.NoError(t, err) require.Equal(t, *result, tt.expected) } else { require.ErrorIs(t, err, tt.err) } } } ================================================ FILE: internal/geo/location/cache.go ================================================ package location import ( "encoding/json" "errors" "fmt" "os" "path" "strconv" "strings" "github.com/samonzeweb/godb" "github.com/samonzeweb/godb/adapters/sqlite" log "github.com/sirupsen/logrus" "github.com/zsefvlol/timezonemapper" "github.com/chubin/wttr.in/internal/config" "github.com/chubin/wttr.in/internal/types" ) // Cache is an implemenation of DB/file-based cache. // // At the moment, it is an implementation for the location cache, // but it should be generalized to cache everything. type Cache struct { config *config.Config db *godb.DB searcher *Searcher indexField string filesCacheDir string } // NewCache returns new cache reader for the specified config. func NewCache(config *config.Config) (*Cache, error) { var ( db *godb.DB err error ) if config.Geo.LocationCacheType == types.CacheTypeDB { log.Debugln("using db for location cache") db, err = godb.Open(sqlite.Adapter, config.Geo.LocationCacheDB) if err != nil { return nil, err } log.Debugln("db file:", config.Geo.LocationCacheDB) // Needed for "upsert" implementation in Put() db.UseErrorParser() } return &Cache{ config: config, db: db, indexField: "name", filesCacheDir: config.Geo.LocationCache, searcher: NewSearcher(config), }, nil } // Resolve returns location information for specified location. // If the information is found in the cache, it is returned. // If it is not found, the external service is queried, // and the result is stored in the cache. func (c *Cache) Resolve(location string) (*Location, error) { location = normalizeLocationName(location) loc, err := c.Read(location) if !errors.Is(err, types.ErrNotFound) { return loc, err } log.Debugln("geo/location: not found in cache:", location) loc, err = c.searcher.Search(location) if err != nil { return nil, err } loc.Name = location loc.Timezone = latLngToTimezoneString(loc.Lat, loc.Lon) err = c.Put(location, loc) if err != nil { return nil, err } return loc, nil } // Read returns location information from the cache, if found, // or types.ErrNotFound if not found. If the entry is found, but its format // is invalid, types.ErrInvalidCacheEntry is returned. func (c *Cache) Read(addr string) (*Location, error) { if c.config.Geo.LocationCacheType == types.CacheTypeFiles { return c.readFromCacheFile(addr) } return c.readFromCacheDB(addr) } func (c *Cache) readFromCacheFile(name string) (*Location, error) { var ( fileLoc = struct { Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` Timezone string `json:"timezone"` Address string `json:"address"` }{} location Location ) bytes, err := os.ReadFile(c.cacheFile(name)) if err != nil { return nil, types.ErrNotFound } err = json.Unmarshal(bytes, &fileLoc) if err != nil { return nil, err } // normalize name name = strings.TrimSpace( strings.TrimRight( strings.TrimLeft(name, `"`), `"`)) timezone := fileLoc.Timezone if timezone == "" { timezone = timezonemapper.LatLngToTimezoneString(fileLoc.Latitude, fileLoc.Longitude) } location = Location{ Name: name, Lat: fmt.Sprint(fileLoc.Latitude), Lon: fmt.Sprint(fileLoc.Longitude), Timezone: timezone, Fullname: fileLoc.Address, } return &location, nil } func (c *Cache) readFromCacheDB(addr string) (*Location, error) { result := Location{} err := c.db.Select(&result). Where(c.indexField+" = ?", addr). Do() if strings.Contains(fmt.Sprint(err), "no rows in result set") { return nil, types.ErrNotFound } if err != nil { return nil, fmt.Errorf("readFromCacheDB: %w", err) } return &result, nil } func (c *Cache) Put(addr string, loc *Location) error { log.Infoln("geo/location: storing in cache:", loc) if c.config.Geo.IPCacheType == types.CacheTypeDB { return c.putToCacheDB(loc) } return c.putToCacheFile(addr, loc) } func (c *Cache) putToCacheDB(loc *Location) error { err := c.db.Insert(loc).Do() if strings.Contains(fmt.Sprint(err), "UNIQUE constraint failed") { return c.db.Update(loc).Do() } return err } func (c *Cache) putToCacheFile(addr string, loc fmt.Stringer) error { return os.WriteFile(c.cacheFile(addr), []byte(loc.String()), 0o600) } // cacheFile returns path to the cache entry for addr. func (c *Cache) cacheFile(item string) string { return path.Join(c.filesCacheDir, item) } // normalizeLocationName converts name into the standard location form // with the following steps: // - remove excessive spaces, // - remove quotes, // - convert to lover case. func normalizeLocationName(name string) string { name = strings.ReplaceAll(name, `"`, " ") name = strings.ReplaceAll(name, `'`, " ") name = strings.TrimSpace(name) name = strings.Join(strings.Fields(name), " ") return strings.ToLower(name) } // latLngToTimezoneString returns timezone for lat, lon, // or an empty string if they are invalid. func latLngToTimezoneString(lat, lon string) string { latFloat, err := strconv.ParseFloat(lat, 64) if err != nil { log.Errorln("geoloc: latLngToTimezoneString:", err) return "" } lonFloat, err := strconv.ParseFloat(lon, 64) if err != nil { log.Errorln("geoloc: latLngToTimezoneString:", err) return "" } return timezonemapper.LatLngToTimezoneString(latFloat, lonFloat) } ================================================ FILE: internal/geo/location/convert.go ================================================ package location import ( "database/sql" "errors" "fmt" "log" "os" "path/filepath" "strings" "github.com/samonzeweb/godb" "github.com/samonzeweb/godb/adapters/sqlite" ) // ConvertCache converts files-based cache into the DB-based cache. // If reset is true, the DB cache is created from scratch. // //nolint:funlen,cyclop func (c *Cache) ConvertCache(reset bool) error { var ( dbfile = c.config.Geo.LocationCacheDB tableName = "Location" cacheFiles = c.filesCacheDir known = map[string]bool{} ) if reset { err := removeDBIfExists(dbfile) if err != nil { return err } } db, err := godb.Open(sqlite.Adapter, dbfile) if err != nil { return err } if reset { err = createTable(db, tableName) if err != nil { return err } } log.Println("listing cache entries...") files, err := filepath.Glob(filepath.Join(cacheFiles, "*")) if err != nil { return err } log.Printf("going to convert %d entries\n", len(files)) block := []Location{} for i, file := range files { ip := filepath.Base(file) loc, err := c.Read(ip) if err != nil { log.Println("invalid entry for", ip) continue } // Skip too long location names. if len(loc.Name) > 25 { continue } // Skip duplicates. if known[loc.Name] { log.Println("skipping", loc.Name) continue } singleLocation := Location{} err = db.Select(&singleLocation). Where("name = ?", loc.Name). Do() if !errors.Is(err, sql.ErrNoRows) { log.Println("found in db:", loc.Name) continue } known[loc.Name] = true // Skip some invalid names. if strings.Contains(loc.Name, "\n") { continue } block = append(block, *loc) if i%1000 != 0 || i == 0 { continue } log.Println("going to insert new entries") err = db.BulkInsert(&block).Do() if err != nil { return err } block = []Location{} log.Println("converted", i+1, "entries") } // inserting the rest. err = db.BulkInsert(&block).Do() if err != nil { return err } log.Println("converted", len(files), "entries") return nil } func createTable(db *godb.DB, tableName string) error { createTable := fmt.Sprintf( `create table %s ( name text not null primary key, displayName text not null, lat text not null, lon text not null, timezone text not null); `, tableName) _, err := db.CurrentDB().Exec(createTable) return err } func removeDBIfExists(filename string) error { _, err := os.Stat(filename) if err != nil { if !os.IsNotExist(err) { return err } // no db file return nil } return os.Remove(filename) } ================================================ FILE: internal/geo/location/location.go ================================================ package location import ( "encoding/json" "log" ) type Location struct { Name string `db:"name,key" json:"name"` Lat string `db:"lat" json:"latitude"` Lon string `db:"lon" json:"longitude"` Timezone string `db:"timezone" json:"timezone"` Fullname string `db:"displayName" json:"address"` } // String returns string representation of location. func (l *Location) String() string { bytes, err := json.Marshal(l) if err != nil { // should never happen log.Fatalln(err) } return string(bytes) } ================================================ FILE: internal/geo/location/nominatim.go ================================================ package location import ( "encoding/json" "fmt" "io/ioutil" "net/http" "github.com/chubin/wttr.in/internal/types" log "github.com/sirupsen/logrus" ) type Nominatim struct { name string url string token string typ string } type locationQuerier interface { Query(*Nominatim, string) (*Location, error) } func NewNominatim(name, typ, url, token string) *Nominatim { return &Nominatim{ name: name, url: url, token: token, typ: typ, } } func (n *Nominatim) Query(location string) (*Location, error) { var data locationQuerier switch n.typ { case "iq": data = &locationIQ{} case "opencage": data = &locationOpenCage{} default: return nil, fmt.Errorf("%s: %w", n.name, types.ErrUnknownLocationService) } return data.Query(n, location) } func makeQuery(url string, result interface{}) error { var errResponse struct { Error string } log.Debugln("nominatim:", url) resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } err = json.Unmarshal(body, &errResponse) if err == nil && errResponse.Error != "" { return fmt.Errorf("%w: %s", types.ErrUpstream, errResponse.Error) } log.Debugln("nominatim: response: ", string(body)) err = json.Unmarshal(body, &result) if err != nil { return err } return nil } ================================================ FILE: internal/geo/location/nominatim_locationiq.go ================================================ package location import ( "fmt" "net/url" "github.com/chubin/wttr.in/internal/types" ) type locationIQ []struct { Name string `db:"name,key"` Lat string `db:"lat"` Lon string `db:"lon"` //nolint:tagliatelle Fullname string `db:"displayName" json:"display_name"` } func (data *locationIQ) Query(n *Nominatim, location string) (*Location, error) { url := fmt.Sprintf( "%s?q=%s&format=json&language=native&limit=1&key=%s", n.url, url.QueryEscape(location), n.token) err := makeQuery(url, data) if err != nil { return nil, fmt.Errorf("%s: %w", n.name, err) } if len(*data) != 1 { return nil, fmt.Errorf("%w: %s: invalid response", types.ErrUpstream, n.name) } nl := &(*data)[0] return &Location{ Lat: nl.Lat, Lon: nl.Lon, Fullname: nl.Fullname, }, nil } ================================================ FILE: internal/geo/location/nominatim_opencage.go ================================================ package location import ( "fmt" "net/url" "github.com/chubin/wttr.in/internal/types" ) type locationOpenCage struct { Results []struct { Name string `db:"name,key"` Geometry struct { Lat float64 `db:"lat"` Lng float64 `db:"lng"` } Fullname string `json:"formatted"` } `json:"results"` } func (data *locationOpenCage) Query(n *Nominatim, location string) (*Location, error) { url := fmt.Sprintf( "%s?q=%s&language=native&limit=1&key=%s", n.url, url.QueryEscape(location), n.token) err := makeQuery(url, data) if err != nil { return nil, fmt.Errorf("%s: %w", n.name, err) } if len(data.Results) != 1 { return nil, fmt.Errorf("%w: %s: invalid response", types.ErrUpstream, n.name) } nl := data.Results[0] return &Location{ Lat: fmt.Sprint(nl.Geometry.Lat), Lon: fmt.Sprint(nl.Geometry.Lng), Fullname: nl.Fullname, }, nil } ================================================ FILE: internal/geo/location/response.go ================================================ package location import ( "encoding/json" "fmt" "log" "net/http" "strings" "github.com/chubin/wttr.in/internal/routing" ) // Response provides routing interface to the geo cache. func (c *Cache) Response(r *http.Request) *routing.Cadre { var ( locationName = r.URL.Query().Get("location") loc *Location bytes []byte err error ) if locationName == "" { return errorResponse("location is not specified") } if strings.Contains(locationName, ".html") || strings.Contains(locationName, ".txt") { return errorResponse("invalid location") } loc, err = c.Resolve(locationName) if err != nil { log.Println("geo/location error:", locationName, r.RemoteAddr) return errorResponse(fmt.Sprint(err)) } bytes, err = json.Marshal(loc) if err != nil { return errorResponse(fmt.Sprint(err)) } return &routing.Cadre{Body: bytes} } func errorResponse(s string) *routing.Cadre { return &routing.Cadre{Body: []byte( fmt.Sprintf(`{"error": %q}`, s), )} } ================================================ FILE: internal/geo/location/search.go ================================================ package location import "github.com/chubin/wttr.in/internal/config" type Provider interface { Query(location string) (*Location, error) } type Searcher struct { providers []Provider } // NewSearcher returns a new Searcher for the specified config. func NewSearcher(config *config.Config) *Searcher { providers := []Provider{} for _, p := range config.Geo.Nominatim { providers = append(providers, NewNominatim(p.Name, p.Type, p.URL, p.Token)) } return &Searcher{ providers: providers, } } // Search makes queries through all known providers, // and returns response, as soon as it is not nil. // If all responses were nil, the last response is returned. func (s *Searcher) Search(location string) (*Location, error) { var ( err error result *Location ) for _, p := range s.providers { result, err = p.Query(location) if result != nil && err == nil { return result, nil } } return result, err } ================================================ FILE: internal/logging/logging.go ================================================ package logging import ( "fmt" "net/http" "os" "sync" "time" "github.com/chubin/wttr.in/internal/util" ) // Logging request. // // RequestLogger logs all incoming HTTP requests. type RequestLogger struct { buf map[logEntry]int filename string m sync.Mutex period time.Duration lastFlush time.Time } type logEntry struct { Proto string IP string URI string UserAgent string } // NewRequestLogger returns a new RequestLogger for the specified log file. // Flush logging entries after period of time. // // If filename is empty, no log will be written, and all logging entries // will be silently dropped. func NewRequestLogger(filename string, period time.Duration) *RequestLogger { return &RequestLogger{ buf: map[logEntry]int{}, filename: filename, m: sync.Mutex{}, period: period, } } // Log logs information about a HTTP request. func (rl *RequestLogger) Log(r *http.Request) error { le := logEntry{ Proto: "http", IP: util.ReadUserIP(r), URI: r.RequestURI, UserAgent: r.Header.Get("User-Agent"), } if r.TLS != nil { le.Proto = "https" } // Do not log 127.0.0.1 connections if le.IP == "127.0.0.1" { return nil } rl.m.Lock() rl.buf[le]++ rl.m.Unlock() if time.Since(rl.lastFlush) > rl.period { return rl.flush() } return nil } // flush stores log data to disk, and flushes the buffer. func (rl *RequestLogger) flush() error { rl.m.Lock() defer rl.m.Unlock() // It is possible, that while waiting the mutex, // the buffer was already flushed. if time.Since(rl.lastFlush) <= rl.period { return nil } if rl.filename != "" { // Generate log output. output := "" for k, hitsNumber := range rl.buf { output += fmt.Sprintf("%s %3d %s\n", time.Now().Format(time.RFC3339), hitsNumber, k.String()) } // Open log file. //nolint:nosnakecase f, err := os.OpenFile(rl.filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) if err != nil { return err } defer f.Close() // Save output to log file. _, err = f.Write([]byte(output)) if err != nil { return err } } // Flush buffer. rl.buf = map[logEntry]int{} rl.lastFlush = time.Now() return nil } // String returns string representation of logEntry. func (e *logEntry) String() string { return fmt.Sprintf( "%s %s %s %s", e.Proto, e.IP, e.URI, e.UserAgent, ) } ================================================ FILE: internal/logging/suppress.go ================================================ package logging import ( "os" "strings" "sync" ) // LogSuppressor provides io.Writer interface for logging // with lines suppression. For usage with log.Logger. type LogSuppressor struct { filename string suppress []string linePrefix string logFile *os.File m sync.Mutex } // NewLogSuppressor creates a new LogSuppressor for specified // filename and lines to be suppressed. // // If filename is empty, log entries will be printed to stderr. func NewLogSuppressor(filename string, suppress []string, linePrefix string) *LogSuppressor { return &LogSuppressor{ filename: filename, suppress: suppress, linePrefix: linePrefix, } } // Open opens log file. func (ls *LogSuppressor) Open() error { var err error if ls.filename == "" { return nil } //nolint:nosnakecase ls.logFile, err = os.OpenFile(ls.filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o600) return err } // Close closes log file. func (ls *LogSuppressor) Close() error { if ls.filename == "" { return nil } return ls.logFile.Close() } // Write writes p to log, and returns number f bytes written. // Implements io.Writer interface. func (ls *LogSuppressor) Write(p []byte) (int, error) { var output string if ls.filename == "" { return os.Stdin.Write(p) } ls.m.Lock() defer ls.m.Unlock() lines := strings.Split(string(p), ls.linePrefix) for _, line := range lines { if (func(line string) bool { for _, suppress := range ls.suppress { if strings.Contains(line, suppress) { return true } } return false })(line) { continue } output += line } return ls.logFile.Write([]byte(output)) } ================================================ FILE: internal/options/options.go ================================================ package options import ( "fmt" "io/ioutil" "gopkg.in/yaml.v3" ) // WttrInOptions represents the configuration for wttr.in query options and format specifiers. type WttrInOptions struct { QueryOptions []QueryOption `yaml:"query_options"` FormatSpecifiers []FormatSpecifier `yaml:"format_specifiers"` } // QueryOption defines a single query option for wttr.in, such as `lang` or `format`. type QueryOption struct { // Name is the long name of the option (e.g., "lang", "current_only"). Name string `yaml:"name"` // Short is the short name of the option (e.g., "m"), omitted if not present. Short string `yaml:"short,omitempty"` // Description of the option's purpose (e.g., "Specify output language"). Description string `yaml:"description"` // Type is the data type of the option (e.g., "boolean", "string", "integer"). Type string `yaml:"type"` // ValuesMap is the map of possible values to their descriptions (e.g., for `lang`, `format`). ValuesMap map[string]string `yaml:"values_map,omitempty"` // Values is the list of supported values (e.g., ["true", "false"] for booleans). Values []string `yaml:"values,omitempty"` // Range is the numeric range for integer options (e.g., min and max for `transparency`). Range *Range `yaml:"range,omitempty"` // Default is the default value of the option (e.g., "en" for `lang`, null for `background`). Default interface{} `yaml:"default"` // Validate contains validation conditions in function call style (e.g., "length 6" for `background`). Validate []string `yaml:"validate,omitempty"` // Active indicates if the option is implemented (true) or proposed (false). Active bool `yaml:"active"` // Note includes additional notes or caveats (e.g., "Proposed but not officially supported"). Note string `yaml:"note,omitempty"` } // Range defines a numeric range for integer-type query options. type Range struct { // Min is the minimum value of the range (e.g., 0 for `transparency`). Min int `yaml:"min"` // Max is the maximum value of the range, nullable for unbounded (e.g., null for `date`). Max *int `yaml:"max"` } // FormatSpecifier defines a format specifier for the `format` option (e.g., %c, %t). type FormatSpecifier struct { // Specifier is the format specifier (e.g., "%c"). Specifier string `yaml:"specifier"` // Description provides the output description of the specifier (e.g., "Weather condition"). Description string `yaml:"description"` // Active indicates if the specifier is implemented (true) or proposed (false). Active bool `yaml:"active"` // Note contains additional notes, if any (e.g., "Proposed in issue #585"). Note string `yaml:"note,omitempty"` } // NewFromFile reads a QueryOption from a YAML file and returns a pointer to it. func NewFromFile(filename string) (*WttrInOptions, error) { // Read the YAML file data, err := ioutil.ReadFile(filename) if err != nil { return nil, fmt.Errorf("failed to read file: %w", err) } // Unmarshal the YAML content into a QueryOption struct var option WttrInOptions if err := yaml.Unmarshal(data, &option); err != nil { return nil, fmt.Errorf("failed to unmarshal YAML: %w", err) } return &option, nil } ================================================ FILE: internal/options/parse.go ================================================ package options import ( "errors" "fmt" "net/url" "regexp" "strconv" "strings" ) // Errors for query parsing and validation var ( ErrInvalidQueryString = errors.New("ERR001: failed to parse query string") ErrMultipleValues = errors.New("ERR002: option has multiple values") ErrUnknownOption = errors.New("ERR003: unknown option") ErrOptionNotImplemented = errors.New("ERR004: option is not implemented") ErrBooleanValueRequired = errors.New("ERR005: option requires boolean value (true/false)") ErrInvalidBooleanValue = errors.New("ERR006: invalid boolean value") ErrValueRequired = errors.New("ERR007: option requires a value") ErrInvalidStringValue = errors.New("ERR008: invalid string value") ErrInvalidValidationRule = errors.New("ERR009: invalid validation rule") ErrInvalidLengthRule = errors.New("ERR010: invalid length rule") ErrInvalidLength = errors.New("ERR011: invalid length") ErrInvalidRegexpRule = errors.New("ERR012: invalid regexp rule") ErrInvalidRegexp = errors.New("ERR013: invalid regexp match") ErrUnsupportedRule = errors.New("ERR014: unsupported validation rule") ErrInvalidInteger = errors.New("ERR015: option requires an integer") ErrBelowMinimum = errors.New("ERR016: value below minimum") ErrAboveMaximum = errors.New("ERR017: value exceeds maximum") ErrUnsupportedType = errors.New("ERR018: unsupported option type") ErrInactiveFormatSpecifier = errors.New("ERR019: format specifier is not implemented") ) // ParseQueryString parses a wttr.in query string into a map of option names to values. // It validates option names, types, content, and values based on the provided WttrInOptions. // Returns the parsed map or an error if parsing or validation fails. func ParseQueryString(query string, config *WttrInOptions) (map[string]string, error) { // Initialize result map and lookup tables result := make(map[string]string) shortToName, nameToOption := buildOptionLookups(config.QueryOptions) // Parse query string query = strings.ReplaceAll( strings.ReplaceAll(query, "%25", "%"), "%", "%25") parsed, err := url.ParseQuery(query) if err != nil { return nil, fmt.Errorf("%w: %v", ErrInvalidQueryString, err) } // Process query parameters for key, values := range parsed { if len(values) == 0 || (len(values) == 1 && values[0] == "") { if err := handleFlagOption(key, shortToName, nameToOption, result); err != nil { return nil, err } continue } if len(values) > 1 { return nil, fmt.Errorf("%w: %s: %v", ErrMultipleValues, key, values) } if err := handleValueOption(key, values[0], shortToName, nameToOption, result); err != nil { return nil, err } } // Validate format specifiers if format=custom if formatValue, exists := result["format"]; exists && formatValue == "custom" { for _, spec := range config.FormatSpecifiers { if !spec.Active { return nil, fmt.Errorf("%w: %s", ErrInactiveFormatSpecifier, spec.Specifier) } } } return result, nil } // buildOptionLookups creates lookup maps for short-to-long names and option details. func buildOptionLookups(options []QueryOption) (map[string]string, map[string]QueryOption) { shortToName := make(map[string]string) nameToOption := make(map[string]QueryOption) for _, opt := range options { nameToOption[opt.Name] = opt if opt.Short != "" { shortToName[opt.Short] = opt.Name } } return shortToName, nameToOption } // handleFlagOption processes flag options (e.g., "0pq" or "m") without values. func handleFlagOption(key string, shortToName map[string]string, nameToOption map[string]QueryOption, result map[string]string) error { // Handle bundled short options (e.g., "0pq") if len(key) > 1 && !strings.Contains(key, "=") { for _, short := range strings.Split(key, "") { if err := validateAndSetFlag(short, shortToName, nameToOption, result); err != nil { return err } } return nil } // Handle single flag return validateAndSetFlag(key, shortToName, nameToOption, result) } // validateAndSetFlag validates and sets a single flag option. func validateAndSetFlag(key string, shortToName map[string]string, nameToOption map[string]QueryOption, result map[string]string) error { name, exists := shortToName[key] if !exists { name = key // Try long name } opt, exists := nameToOption[name] if !exists { return fmt.Errorf("%w: %s", ErrUnknownOption, key) } if !opt.Active { return fmt.Errorf("%w: %s", ErrOptionNotImplemented, name) } if opt.Type != "boolean" { return fmt.Errorf("%w: %s", ErrValueRequired, key) } result[name] = "true" return nil } // handleValueOption processes options with values (e.g., "lang=fr"). func handleValueOption(key, value string, shortToName map[string]string, nameToOption map[string]QueryOption, result map[string]string) error { // Resolve option name name, exists := shortToName[key] if !exists { name = key // Assume long name } opt, exists := nameToOption[name] if !exists { return fmt.Errorf("%w: %s", ErrUnknownOption, key) } if !opt.Active { return fmt.Errorf("%w: %s", ErrOptionNotImplemented, name) } // Validate based on type switch opt.Type { case "boolean": return validateBooleanOption(name, value, opt, result) case "string": return validateStringOption(name, value, opt, result) case "integer": return validateIntegerOption(name, value, opt, result) default: return fmt.Errorf("%w: %s: %s", ErrUnsupportedType, name, opt.Type) } } // validateBooleanOption validates and sets boolean option values. func validateBooleanOption(name, value string, opt QueryOption, result map[string]string) error { if value != "true" && value != "false" { return fmt.Errorf("%w: %s: %s", ErrBooleanValueRequired, name, value) } if len(opt.Values) > 0 { for _, v := range opt.Values { if v == value { result[name] = value return nil } } return fmt.Errorf("%w: %s: %s, expected one of %v", ErrInvalidBooleanValue, name, value, opt.Values) } result[name] = value return nil } // validateStringOption validates and sets string option values, including background-specific rules. func validateStringOption(name, value string, opt QueryOption, result map[string]string) error { if len(opt.ValuesMap) > 0 { if _, valid := opt.ValuesMap[value]; !valid { return fmt.Errorf("%w: %s: %s, expected one of %v", ErrInvalidStringValue, name, value, getMapKeys(opt.ValuesMap)) } } if len(opt.Values) > 0 { for _, v := range opt.Values { if v == value { result[name] = value return nil } } return fmt.Errorf("%w: %s: %s, expected one of %v", ErrInvalidStringValue, name, value, opt.Values) } if name == "background" && len(opt.Validate) > 0 { for _, rule := range opt.Validate { if err := applyValidationRule(name, value, rule); err != nil { return err } } } result[name] = value return nil } // applyValidationRule applies a single validation rule (e.g., "length 6", "regexp [0-9a-fA-F]{6}"). func applyValidationRule(name, value, rule string) error { parts := strings.SplitN(rule, " ", 2) if len(parts) < 2 { return fmt.Errorf("%w: %s: %s", ErrInvalidValidationRule, name, rule) } switch parts[0] { case "length": length, err := strconv.Atoi(parts[1]) if err != nil { return fmt.Errorf("%w: %s: %s", ErrInvalidLengthRule, name, rule) } if len(value) != length { return fmt.Errorf("%w: %s: %s, expected length %d", ErrInvalidLength, name, value, length) } case "regexp": re, err := regexp.Compile(parts[1]) if err != nil { return fmt.Errorf("%w: %s: %s", ErrInvalidRegexpRule, name, rule) } if !re.MatchString(value) { return fmt.Errorf("%w: %s: %s, expected match for %s", ErrInvalidRegexp, name, value, parts[1]) } default: return fmt.Errorf("%w: %s: %s", ErrUnsupportedRule, name, rule) } return nil } // validateIntegerOption validates and sets integer option values. func validateIntegerOption(name, value string, opt QueryOption, result map[string]string) error { num, err := strconv.Atoi(value) if err != nil { return fmt.Errorf("%w: %s: %s", ErrInvalidInteger, name, value) } if opt.Range != nil { if num < opt.Range.Min { return fmt.Errorf("%w: %s: %d, minimum is %d", ErrBelowMinimum, name, num, opt.Range.Min) } if opt.Range.Max != nil && num > *opt.Range.Max { return fmt.Errorf("%w: %s: %d, maximum is %d", ErrAboveMaximum, name, num, *opt.Range.Max) } } result[name] = value return nil } // getMapKeys returns a sorted slice of keys from a map for error messages. func getMapKeys(m map[string]string) []string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } // Sort for consistent error messages for i := 0; i < len(keys)-1; i++ { for j := i + 1; j < len(keys); j++ { if keys[i] > keys[j] { keys[i], keys[j] = keys[j], keys[i] } } } return keys } ================================================ FILE: internal/options/processlog.go ================================================ package options import ( "bufio" "fmt" "os" "strings" ) // ProcessLogFile reads a wttr.in log file, parses queries, and writes invalid entries to an error file. // Each log line is expected in the format: "timestamp request_id protocol ip query user_agent". // Invalid queries and their error messages are written to errorFilePath. func ProcessLogFile(logFilePath, errorFilePath string, config *WttrInOptions) error { logFile, err := openLogFile(logFilePath) if err != nil { return err } defer logFile.Close() errorFile, writer, err := openErrorFile(errorFilePath) if err != nil { return err } defer errorFile.Close() defer writer.Flush() return processLogLines(logFile, writer, config) } // openLogFile opens the log file for reading. func openLogFile(logFilePath string) (*os.File, error) { logFile, err := os.Open(logFilePath) if err != nil { return nil, fmt.Errorf("failed to open log file: %w", err) } return logFile, nil } // openErrorFile creates and opens the error file for writing. func openErrorFile(errorFilePath string) (*os.File, *bufio.Writer, error) { errorFile, err := os.Create(errorFilePath) if err != nil { return nil, nil, fmt.Errorf("failed to create error file: %w", err) } writer := bufio.NewWriter(errorFile) return errorFile, writer, nil } // processLogLines reads and processes each line of the log file. func processLogLines(logFile *os.File, writer *bufio.Writer, config *WttrInOptions) error { scanner := bufio.NewScanner(logFile) lineNumber := 0 for scanner.Scan() { lineNumber++ if err := processLogLine(scanner.Text(), lineNumber, writer, config); err != nil { return err } } if err := scanner.Err(); err != nil { return fmt.Errorf("failed to read log file: %w", err) } return nil } // processLogLine processes a single log line and writes errors to the error file. func processLogLine(line string, lineNumber int, writer *bufio.Writer, config *WttrInOptions) error { query, err := extractQueryFromLine(line, lineNumber, writer) if err != nil { return err } _, err = ParseQueryString(query, config) if err != nil { _, writeErr := fmt.Fprintf(writer, "Line %d: Query: %s, Error: %v\n", lineNumber, query, err) if writeErr != nil { return fmt.Errorf("failed to write to error file at line %d: %w", lineNumber, writeErr) } } return nil } // extractQueryFromLine extracts the query from a log line and validates its format. func extractQueryFromLine(line string, lineNumber int, writer *bufio.Writer) (string, error) { fields := strings.Fields(line) if len(fields) < 5 { _, err := fmt.Fprintf(writer, "Line %d: Invalid log format: %s\n", lineNumber, line) if err != nil { return "", fmt.Errorf("failed to write to error file at line %d: %w", lineNumber, err) } return "", nil } query := fields[4] if strings.Contains(query, "?") { parts := strings.SplitN(query, "?", 2) if len(parts) == 2 { return parts[1], nil } return "", nil } return "", nil } ================================================ FILE: internal/processor/j1.go ================================================ package processor import ( "fmt" "io/ioutil" "net/http" "net/url" "strings" "time" ) func getAny(req *http.Request, tr1, tr2, tr3, tr4 *http.Transport) (*ResponseWithHeader, error) { uri := strings.ReplaceAll(req.URL.RequestURI(), "%", "%25") u, err := url.Parse(uri) if err != nil { return nil, err } format := u.Query().Get("format") if format == "j1" { return getJ1(req, tr1) } else if format != "" { return getFormat(req, tr2) } // log.Println(req.URL.Query()) // log.Println() if checkURLForPNG(req) { return getDefault(req, tr4) } return getDefault(req, tr3) } func getJ1(req *http.Request, transport *http.Transport) (*ResponseWithHeader, error) { return getUpstream(req, transport) } func getFormat(req *http.Request, transport *http.Transport) (*ResponseWithHeader, error) { return getUpstream(req, transport) } func getDefault(req *http.Request, transport *http.Transport) (*ResponseWithHeader, error) { return getUpstream(req, transport) } func getUpstream(req *http.Request, transport *http.Transport) (*ResponseWithHeader, error) { client := &http.Client{ Transport: transport, } queryURL := fmt.Sprintf("http://%s%s", req.Host, req.RequestURI) proxyReq, err := http.NewRequest(req.Method, queryURL, req.Body) if err != nil { return nil, err } // proxyReq.Header.Set("Host", req.Host) // proxyReq.Header.Set("X-Forwarded-For", req.RemoteAddr) for header, values := range req.Header { for _, value := range values { proxyReq.Header.Add(header, value) } } if proxyReq.Header.Get("X-Forwarded-For") == "" { proxyReq.Header.Set("X-Forwarded-For", ipFromAddr(req.RemoteAddr)) } res, err := client.Do(proxyReq) if err != nil { return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } return &ResponseWithHeader{ InProgress: false, Expires: time.Now().Add(time.Duration(randInt(1000, 1500)) * time.Second), Body: body, Header: res.Header, StatusCode: res.StatusCode, }, nil } func checkURLForPNG(r *http.Request) bool { url := r.URL.String() return strings.Contains(url, ".png") && !strings.Contains(url, "/files/") } ================================================ FILE: internal/processor/peak.go ================================================ package processor import ( "log" "net/http" "sync" "time" "github.com/robfig/cron" ) func (rp *RequestProcessor) startPeakHandling() error { var err error c := cron.New() // cronTime := fmt.Sprintf("%d,%d * * * *", 30-prefetchInterval/60, 60-prefetchInterval/60) err = c.AddFunc( "24 * * * *", func() { rp.prefetchPeakRequests(&rp.peakRequest30) }, ) if err != nil { return err } err = c.AddFunc( "54 * * * *", func() { rp.prefetchPeakRequests(&rp.peakRequest60) }, ) if err != nil { return err } c.Start() return nil } // registerPeakRequest registers requests coming in the peak time. // Such requests can be prefetched afterwards just before the peak time comes. func (rp *RequestProcessor) savePeakRequest(cacheDigest string, r *http.Request) { if _, min, _ := time.Now().Clock(); min == 30 { rp.peakRequest30.Store(cacheDigest, *r) } else if min == 0 { rp.peakRequest60.Store(cacheDigest, *r) } } func (rp *RequestProcessor) prefetchRequest(r *http.Request) error { _, err := rp.ProcessRequest(r) return err } func syncMapLen(sm *sync.Map) int { count := 0 f := func(key, value interface{}) bool { // Not really certain about this part, don't know for sure // if this is a good check for an entry's existence if key == "" { return false } count++ return true } sm.Range(f) return count } func (rp *RequestProcessor) prefetchPeakRequests(peakRequestMap *sync.Map) { peakRequestLen := syncMapLen(peakRequestMap) if peakRequestLen == 0 { return } log.Printf("PREFETCH: Prefetching %d requests\n", peakRequestLen) sleepBetweenRequests := time.Duration(rp.config.Uplink.PrefetchInterval*1000/peakRequestLen) * time.Millisecond peakRequestMap.Range(func(key interface{}, value interface{}) bool { req, ok := value.(http.Request) if !ok { log.Println("missing value for:", key) return true } go func(r http.Request) { err := rp.prefetchRequest(&r) if err != nil { log.Println("prefetch request:", err) } }(req) peakRequestMap.Delete(key) time.Sleep(sleepBetweenRequests) return true }) } ================================================ FILE: internal/processor/processor.go ================================================ package processor import ( "context" "fmt" "log" "math/rand" "net" "net/http" "strings" "sync" "time" lru "github.com/hashicorp/golang-lru" "github.com/chubin/wttr.in/internal/config" geoip "github.com/chubin/wttr.in/internal/geo/ip" geoloc "github.com/chubin/wttr.in/internal/geo/location" "github.com/chubin/wttr.in/internal/routing" "github.com/chubin/wttr.in/internal/stats" "github.com/chubin/wttr.in/internal/util" ) // plainTextAgents contains signatures of the plain-text agents. func plainTextAgents() []string { return []string{ "curl", "httpie", "lwp-request", "wget", "python-httpx", "python-requests", "openbsd ftp", "powershell", "fetch", "aiohttp", "http_get", "xh", "nushell", "zig", "node", } } type ResponseWithHeader struct { InProgress bool // true if the request is being processed Expires time.Time // expiration time of the cache entry Body []byte Header http.Header StatusCode int // e.g. 200 } // RequestProcessor handles incoming requests. type RequestProcessor struct { peakRequest30 sync.Map peakRequest60 sync.Map lruCache *lru.Cache stats *stats.Stats router routing.Router upstreamTransport1 *http.Transport upstreamTransport2 *http.Transport upstreamTransport3 *http.Transport upstreamTransport4 *http.Transport config *config.Config geoIPCache *geoip.Cache geoLocation *geoloc.Cache } // NewRequestProcessor returns new RequestProcessor. func NewRequestProcessor(config *config.Config) (*RequestProcessor, error) { lruCache, err := lru.New(config.Cache.Size) if err != nil { return nil, err } dialer := &net.Dialer{ Timeout: time.Duration(config.Uplink.Timeout) * time.Second, KeepAlive: time.Duration(config.Uplink.Timeout) * time.Second, DualStack: true, } transport1 := &http.Transport{ DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) { return dialer.DialContext(ctx, network, config.Uplink.Address1) }, } transport2 := &http.Transport{ DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) { return dialer.DialContext(ctx, network, config.Uplink.Address2) }, } transport3 := &http.Transport{ DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) { return dialer.DialContext(ctx, network, config.Uplink.Address3) }, } transport4 := &http.Transport{ DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) { return dialer.DialContext(ctx, network, config.Uplink.Address4) }, } geoCache, err := geoip.NewCache(config) if err != nil { return nil, err } geoLocation, err := geoloc.NewCache(config) if err != nil { return nil, err } rp := &RequestProcessor{ lruCache: lruCache, stats: stats.New(), upstreamTransport1: transport1, upstreamTransport2: transport2, upstreamTransport3: transport3, upstreamTransport4: transport4, config: config, geoIPCache: geoCache, geoLocation: geoLocation, } // Initialize routes. rp.router.AddPath("/:stats", rp.stats) rp.router.AddPath("/:geo-ip-get", rp.geoIPCache) rp.router.AddPath("/:geo-ip-put", rp.geoIPCache) rp.router.AddPath("/:geo-location", rp.geoLocation) return rp, nil } // Start starts async request processor jobs, such as peak handling. func (rp *RequestProcessor) Start() error { return rp.startPeakHandling() } func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader, error) { var ( response *ResponseWithHeader ip = util.ReadUserIP(r) ) if ip != "127.0.0.1" { rp.stats.Inc("total") } // Main routing logic. if rh := rp.router.Route(r); rh != nil { result := rh.Response(r) if result != nil { return fromCadre(result), nil } } if resp, ok := redirectInsecure(r); ok { rp.stats.Inc("redirects") return resp, nil } if dontCache(r) { rp.stats.Inc("uncached") return getAny(r, rp.upstreamTransport1, rp.upstreamTransport2, rp.upstreamTransport3, rp.upstreamTransport4) } // processing cached request cacheDigest := getCacheDigest(r) rp.savePeakRequest(cacheDigest, r) response = rp.processRequestFromCache(r) if response != nil { return response, nil } return rp.processUncachedRequest(r) } // processRequestFromCache processes requests using the cache. // If no entry in cache found, nil is returned. func (rp *RequestProcessor) processRequestFromCache(r *http.Request) *ResponseWithHeader { var ( cacheEntry ResponseWithHeader cacheDigest = getCacheDigest(r) ok bool ) cacheBody, _ := rp.lruCache.Get(cacheDigest) cacheEntry, ok = cacheBody.(ResponseWithHeader) if !ok { return nil } // If after all attempts we still have no answer, // respond with an error message. // (WAS: we try to make the query on our own) for attempts := 0; attempts < 300; attempts++ { if !ok || !cacheEntry.InProgress { break } time.Sleep(30 * time.Millisecond) cacheBody, _ = rp.lruCache.Get(cacheDigest) v, ok := cacheBody.(ResponseWithHeader) if ok { cacheEntry = v } } if cacheEntry.InProgress { // log.Printf("TIMEOUT: %s\n", cacheDigest) return &ResponseWithHeader{ InProgress: false, Expires: time.Now().Add(time.Duration(randInt(1000, 1500)) * time.Second), Body: []byte("This query is already being processed"), StatusCode: 200, } } if ok && !cacheEntry.InProgress && cacheEntry.Expires.After(time.Now()) { rp.stats.Inc("cache1") return &cacheEntry } return nil } // processUncachedRequest processes requests that were not found in the cache. func (rp *RequestProcessor) processUncachedRequest(r *http.Request) (*ResponseWithHeader, error) { var ( cacheDigest = getCacheDigest(r) ip = util.ReadUserIP(r) response *ResponseWithHeader err error ) // Indicate, that the request is being handled. rp.lruCache.Add(cacheDigest, ResponseWithHeader{InProgress: true}) // Response was not found in cache. // Starting real handling. format := r.URL.Query().Get("format") if len(format) != 0 { rp.stats.Inc("format") if format == "j1" { rp.stats.Inc("format=j1") } } // Count, how many IP addresses are known. _, err = rp.geoIPCache.Read(ip) if err == nil { rp.stats.Inc("geoip") } response, err = getAny(r, rp.upstreamTransport1, rp.upstreamTransport2, rp.upstreamTransport3, rp.upstreamTransport4) if err != nil { return nil, err } if response.StatusCode == 200 || response.StatusCode == 304 || response.StatusCode == 404 { rp.lruCache.Add(cacheDigest, *response) } else { log.Printf("REMOVE: %d response for %s from cache\n", response.StatusCode, cacheDigest) rp.lruCache.Remove(cacheDigest) } return response, nil } // getCacheDigest is an implementation of the cache.get_signature of original wttr.in. func getCacheDigest(req *http.Request) string { userAgent := req.Header.Get("User-Agent") queryHost := req.Host queryString := req.RequestURI clientIPAddress := util.ReadUserIP(req) lang := req.Header.Get("Accept-Language") return fmt.Sprintf("%s:%s%s:%s:%s", userAgent, queryHost, queryString, clientIPAddress, lang) } // dontCache returns true if req should not be cached. func dontCache(req *http.Request) bool { // dont cache cyclic requests loc := strings.Split(req.RequestURI, "?")[0] return strings.Contains(loc, ":") } // redirectInsecure returns redirection response, and bool value, if redirection was needed, // if the query comes from a browser, and it is insecure. // // Insecure queries are marked by the frontend web server // with X-Forwarded-Proto header: // `proxy_set_header X-Forwarded-Proto $scheme;`. func redirectInsecure(req *http.Request) (*ResponseWithHeader, bool) { if isPlainTextAgent(req.Header.Get("User-Agent")) { return nil, false } if req.TLS != nil || strings.ToLower(req.Header.Get("X-Forwarded-Proto")) == "https" { return nil, false } target := "https://" + req.Host + req.URL.Path if len(req.URL.RawQuery) > 0 { target += "?" + req.URL.RawQuery } body := []byte(fmt.Sprintf(` 301 Moved

301 Moved

The document has moved here. `, target)) return &ResponseWithHeader{ InProgress: false, Expires: time.Now().Add(time.Duration(randInt(1000, 1500)) * time.Second), Body: body, Header: http.Header{"Location": []string{target}}, StatusCode: 301, }, true } // isPlainTextAgent returns true if userAgent is a plain-text agent. func isPlainTextAgent(userAgent string) bool { userAgentLower := strings.ToLower(userAgent) for _, signature := range plainTextAgents() { if strings.Contains(userAgentLower, signature) { return true } } return false } func randInt(min int, max int) int { return min + rand.Intn(max-min) } // ipFromAddr returns IP address from a ADDR:PORT pair. func ipFromAddr(s string) string { pos := strings.LastIndex(s, ":") if pos == -1 { return s } return s[:pos] } // fromCadre converts Cadre into a responseWithHeader. func fromCadre(cadre *routing.Cadre) *ResponseWithHeader { return &ResponseWithHeader{ Body: cadre.Body, Expires: cadre.Expires, StatusCode: 200, InProgress: false, } } ================================================ FILE: internal/routing/routing.go ================================================ package routing import ( "net/http" "time" ) // CadreFormat specifies how the shot data is formatted. type CadreFormat int const ( // CadreFormatANSI represents Terminal ANSI format. CadreFormatANSI = iota // CadreFormatHTML represents HTML. CadreFormatHTML // CadreFormatPNG represents PNG. CadreFormatPNG ) // Cadre contains result of a query execution. type Cadre struct { // Body contains the data of Cadre, formatted as Format. Body []byte // Format of the shot. Format CadreFormat // Expires contains the time of the Cadre expiration, // or 0 if it does not expire. Expires time.Time } // Handler can handle queries and return views. type Handler interface { Response(*http.Request) *Cadre } type routeFunc func(*http.Request) bool type route struct { routeFunc Handler } // Router keeps a routing table, and finds queries handlers, based on its rules. type Router struct { rt []route } // Route returns a query handler based on its content. func (r *Router) Route(req *http.Request) Handler { for _, re := range r.rt { if re.routeFunc(req) { return re.Handler } } return nil } // AddPath adds route for a static path. func (r *Router) AddPath(path string, handler Handler) { r.rt = append(r.rt, route{routePath(path), handler}) } func routePath(path string) routeFunc { return routeFunc(func(req *http.Request) bool { return req.URL.Path == path }) } ================================================ FILE: internal/stats/stats.go ================================================ package stats import ( "bytes" "fmt" "net/http" "sync" "time" "github.com/chubin/wttr.in/internal/routing" ) // Stats holds processed requests statistics. type Stats struct { m sync.Mutex v map[string]int startTime time.Time } // New returns new Stats. func New() *Stats { return &Stats{ v: map[string]int{}, startTime: time.Now(), } } // Inc key by one. func (c *Stats) Inc(key string) { c.m.Lock() c.v[key]++ c.m.Unlock() } // Get current key counter value. func (c *Stats) Get(key string) int { c.m.Lock() defer c.m.Unlock() return c.v[key] } // Reset key counter. func (c *Stats) Reset(key string) int { c.m.Lock() defer c.m.Unlock() result := c.v[key] c.v[key] = 0 return result } // Show returns current statistics formatted as []byte. func (c *Stats) Show() []byte { var b bytes.Buffer c.m.Lock() defer c.m.Unlock() uptime := time.Since(c.startTime) / time.Second fmt.Fprintf(&b, "%-20s: %v\n", "Running since", c.startTime.Format(time.RFC3339)) fmt.Fprintf(&b, "%-20s: %d\n", "Uptime (min)", uptime/60) fmt.Fprintf(&b, "%-20s: %d\n", "Total queries", c.v["total"]) if uptime != 0 { fmt.Fprintf(&b, "%-20s: %d\n", "Throughput (QpM)", c.v["total"]*60/int(uptime)) } fmt.Fprintf(&b, "%-20s: %d\n", "Cache L1 queries", c.v["cache1"]) if c.v["total"] != 0 { fmt.Fprintf(&b, "%-20s: %d\n", "Cache L1 queries (%)", (100*c.v["cache1"])/c.v["total"]) } fmt.Fprintf(&b, "%-20s: %d\n", "Upstream queries", c.v["total"]-c.v["cache1"]) fmt.Fprintf(&b, "%-20s: %d\n", "Queries with format", c.v["format"]) fmt.Fprintf(&b, "%-20s: %d\n", "Queries with format=j1", c.v["format=j1"]) fmt.Fprintf(&b, "%-20s: %d\n", "Queries with known IP", c.v["geoip"]) return b.Bytes() } func (c *Stats) Response(*http.Request) *routing.Cadre { return &routing.Cadre{ Body: c.Show(), } } ================================================ FILE: internal/types/errors.go ================================================ package types import "errors" var ( ErrNotFound = errors.New("cache entry not found") ErrInvalidCacheEntry = errors.New("invalid cache entry format") ErrUpstream = errors.New("upstream error") // ErrNoServersConfigured means that there are no servers to run. ErrNoServersConfigured = errors.New("no servers configured") ErrUnknownLocationService = errors.New("unknown location service") ) ================================================ FILE: internal/types/types.go ================================================ package types type CacheType string const ( CacheTypeDB = "db" CacheTypeFiles = "files" ) ================================================ FILE: internal/util/files.go ================================================ package util import "os" // RemoveFileIfExists removes filename if exists, or does nothing if the file // is not there. Returns an error, if it occurred during deletion. func RemoveFileIfExists(filename string) error { _, err := os.Stat(filename) if err != nil { if !os.IsNotExist(err) { return err } // no db file return nil } return os.Remove(filename) } ================================================ FILE: internal/util/http.go ================================================ package util import ( "log" "net" "net/http" ) // ReadUserIP returns IP address of the client from http.Request, // taking into account the HTTP headers. func ReadUserIP(r *http.Request) string { IPAddress := r.Header.Get("X-Real-Ip") if IPAddress == "" { IPAddress = r.Header.Get("X-Forwarded-For") } if IPAddress == "" { IPAddress = r.RemoteAddr var err error IPAddress, _, err = net.SplitHostPort(IPAddress) if err != nil { log.Printf("ERROR: userip: %q is not IP:port\n", IPAddress) } } return IPAddress } ================================================ FILE: internal/util/yaml.go ================================================ package util import ( "bytes" "gopkg.in/yaml.v3" ) // YamlUnmarshalStrict unmarshals YAML data with an error when unknown fields are present. func YamlUnmarshalStrict(in []byte, out interface{}) error { dec := yaml.NewDecoder(bytes.NewReader(in)) dec.KnownFields(true) return dec.Decode(out) } ================================================ FILE: internal/view/v1/api.go ================================================ //nolint:forbidigo,funlen,nestif,goerr113,gocognit,cyclop package v1 import ( "bytes" "encoding/json" "flag" "fmt" "io/ioutil" "net/http" "net/url" "os" "strconv" "strings" ) //nolint:tagliatelle type cond struct { ChanceOfRain string `json:"chanceofrain"` FeelsLikeC int `json:",string"` PrecipMM float32 `json:"precipMM,string"` TempC int `json:"tempC,string"` TempC2 int `json:"temp_C,string"` Time int `json:"time,string"` VisibleDistKM int `json:"visibility,string"` WeatherCode int `json:"weatherCode,string"` WeatherDesc []struct{ Value string } WindGustKmph int `json:",string"` Winddir16Point string WindspeedKmph int `json:"windspeedKmph,string"` } type astro struct { Moonrise string Moonset string Sunrise string Sunset string } type weather struct { Astronomy []astro Date string Hourly []cond MaxtempC int `json:"maxtempC,string"` MintempC int `json:"mintempC,string"` } type loc struct { Query string `json:"query"` Type string `json:"type"` } //nolint:tagliatelle type resp struct { Data struct { Cur []cond `json:"current_condition"` Err []struct{ Msg string } `json:"error"` Req []loc `json:"request"` Weather []weather `json:"weather"` } `json:"data"` } func (g *global) getDataFromAPI() (*resp, error) { var ( ret resp params []string ) if len(g.config.APIKey) == 0 { return nil, fmt.Errorf("no API key specified. Setup instructions are in the README") } params = append(params, "key="+g.config.APIKey) // non-flag shortcut arguments will overwrite possible flag arguments for _, arg := range flag.Args() { if v, err := strconv.Atoi(arg); err == nil && len(arg) == 1 { g.config.Numdays = v } else { g.config.City = arg } } if len(g.config.City) > 0 { params = append(params, "q="+url.QueryEscape(g.config.City)) } params = append(params, "format=json", "num_of_days="+strconv.Itoa(g.config.Numdays), "tp=3") if g.config.Lang != "" { params = append(params, "lang="+g.config.Lang) } if g.debug { fmt.Fprintln(os.Stderr, params) } res, err := http.Get(wuri + strings.Join(params, "&")) if err != nil { return nil, err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { return nil, err } if g.debug { var out bytes.Buffer err := json.Indent(&out, body, "", " ") if err != nil { return nil, err } _, err = out.WriteTo(os.Stderr) if err != nil { return nil, err } fmt.Print("\n\n") } if g.config.Lang == "" { if err = json.Unmarshal(body, &ret); err != nil { return nil, err } } else { if err = g.unmarshalLang(body, &ret); err != nil { return nil, err } } return &ret, nil } func (g *global) unmarshalLang(body []byte, r *resp) error { var rv map[string]interface{} if err := json.Unmarshal(body, &rv); err != nil { return err } if data, ok := rv["data"].(map[string]interface{}); ok { if ccs, ok := data["current_condition"].([]interface{}); ok { for _, cci := range ccs { cc, ok := cci.(map[string]interface{}) if !ok { continue } langs, ok := cc["lang_"+g.config.Lang].([]interface{}) if !ok || len(langs) == 0 { continue } weatherDesc, ok := cc["weatherDesc"].([]interface{}) if !ok || len(weatherDesc) == 0 { continue } weatherDesc[0] = langs[0] } } if ws, ok := data["weather"].([]interface{}); ok { for _, wi := range ws { w, ok := wi.(map[string]interface{}) if !ok { continue } if hs, ok := w["hourly"].([]interface{}); ok { for _, hi := range hs { h, ok := hi.(map[string]interface{}) if !ok { continue } langs, ok := h["lang_"+g.config.Lang].([]interface{}) if !ok || len(langs) == 0 { continue } weatherDesc, ok := h["weatherDesc"].([]interface{}) if !ok || len(weatherDesc) == 0 { continue } weatherDesc[0] = langs[0] } } } } } var buf bytes.Buffer if err := json.NewEncoder(&buf).Encode(rv); err != nil { return err } if err := json.NewDecoder(&buf).Decode(r); err != nil { return err } return nil } ================================================ FILE: internal/view/v1/cmd.go ================================================ // This code represents wttr.in view v1. // It is based on wego (github.com/schachmat/wego) from which it diverged back in 2016. //nolint:forbidigo,funlen,gocognit,cyclop package v1 import ( "encoding/json" "errors" "flag" "fmt" "io/ioutil" "log" "os" "os/user" "path" "regexp" "strings" "github.com/mattn/go-colorable" "github.com/mattn/go-runewidth" ) type Configuration struct { APIKey string City string Numdays int Imperial bool WindUnit bool Inverse bool Lang string Narrow bool LocationName string WindMS bool RightToLeft bool } type global struct { ansiEsc *regexp.Regexp config Configuration configpath string debug bool } const ( wuri = "http://127.0.0.1:5001/premium/v1/weather.ashx?" suri = "http://127.0.0.1:5001/premium/v1/search.ashx?" slotcount = 4 ) func (g *global) configload() error { b, err := ioutil.ReadFile(g.configpath) if err == nil { return json.Unmarshal(b, &g.config) } return err } func (g *global) configsave() error { j, err := json.MarshalIndent(g.config, "", "\t") if err == nil { return ioutil.WriteFile(g.configpath, j, 0o600) } return err } func (g *global) init() { flag.IntVar(&g.config.Numdays, "days", 3, "Number of days of weather forecast to be displayed") flag.StringVar(&g.config.Lang, "lang", "en", "Language of the report") flag.StringVar(&g.config.City, "city", "New York", "City to be queried") flag.BoolVar(&g.debug, "debug", false, "Print out raw json response for debugging purposes") flag.BoolVar(&g.config.Imperial, "imperial", false, "Use imperial units") flag.BoolVar(&g.config.Inverse, "inverse", false, "Use inverted colors") flag.BoolVar(&g.config.Narrow, "narrow", false, "Narrow output (two columns)") flag.StringVar(&g.config.LocationName, "location_name", "", "Location name (used in the caption)") flag.BoolVar(&g.config.WindMS, "wind_in_ms", false, "Show wind speed in m/s") flag.BoolVar(&g.config.RightToLeft, "right_to_left", false, "Right to left script") g.configpath = os.Getenv("WEGORC") if g.configpath == "" { usr, err := user.Current() if err != nil { log.Fatalf("%v\nYou can set the environment variable WEGORC to point to your config file as a workaround.", err) } g.configpath = path.Join(usr.HomeDir, ".wegorc") } g.config.APIKey = "" g.config.Imperial = false g.config.Lang = "en" err := g.configload() var pathError *os.PathError if errors.Is(err, pathError) { log.Printf("No config file found. Creating %s ...", g.configpath) if err2 := g.configsave(); err2 != nil { log.Fatal(err2) } } else if err != nil { log.Fatalf("could not parse %v: %v", g.configpath, err) } g.ansiEsc = regexp.MustCompile("\033.*?m") } func Cmd() error { g := global{} g.init() flag.Parse() r, err := g.getDataFromAPI() if err != nil { return err } if r.Data.Req == nil || len(r.Data.Req) < 1 { if r.Data.Err != nil && len(r.Data.Err) >= 1 { log.Fatal(r.Data.Err[0].Msg) } log.Fatal("Malformed response.") } locationName := r.Data.Req[0].Query if g.config.LocationName != "" { locationName = g.config.LocationName } if g.config.Lang == "he" || g.config.Lang == "ar" || g.config.Lang == "fa" { g.config.RightToLeft = true } if caption, ok := localizedCaption()[g.config.Lang]; !ok { fmt.Printf("Weather report: %s\n\n", locationName) } else { if g.config.RightToLeft { caption = locationName + " " + caption space := strings.Repeat(" ", 125-runewidth.StringWidth(caption)) fmt.Printf("%s%s\n\n", space, caption) } else { fmt.Printf("%s %s\n\n", caption, locationName) } } stdout := colorable.NewColorableStdout() if r.Data.Cur == nil || len(r.Data.Cur) < 1 { log.Fatal("No weather data available.") } out := g.formatCond(make([]string, 5), r.Data.Cur[0], true) for _, val := range out { if g.config.RightToLeft { fmt.Fprint(stdout, strings.Repeat(" ", 94)) } else { fmt.Fprint(stdout, " ") } fmt.Fprintln(stdout, val) } if g.config.Numdays == 0 { return nil } if r.Data.Weather == nil { log.Fatal("No detailed weather forecast available.") } for _, d := range r.Data.Weather { lines, err := g.printDay(d) if err != nil { return err } for _, val := range lines { fmt.Fprintln(stdout, val) } } return nil } ================================================ FILE: internal/view/v1/format.go ================================================ //nolint:funlen,nestif,cyclop,gocognit,gocyclo package v1 import ( "fmt" "strings" "unicode/utf8" "github.com/mattn/go-runewidth" ) func windDir() map[string]string { return map[string]string{ "N": "\033[1m↓\033[0m", "NNE": "\033[1m↓\033[0m", "NE": "\033[1m↙\033[0m", "ENE": "\033[1m↙\033[0m", "E": "\033[1m←\033[0m", "ESE": "\033[1m←\033[0m", "SE": "\033[1m↖\033[0m", "SSE": "\033[1m↖\033[0m", "S": "\033[1m↑\033[0m", "SSW": "\033[1m↑\033[0m", "SW": "\033[1m↗\033[0m", "WSW": "\033[1m↗\033[0m", "W": "\033[1m→\033[0m", "WNW": "\033[1m→\033[0m", "NW": "\033[1m↘\033[0m", "NNW": "\033[1m↘\033[0m", } } func (g *global) formatTemp(c cond) string { color := func(temp int, explicitPlus bool) string { var col int //nolint:dupl if !g.config.Inverse { // Extremely cold temperature must be shown with violet // because dark blue is too dark col = 165 switch temp { case -15, -14, -13: col = 171 case -12, -11, -10: col = 33 case -9, -8, -7: col = 39 case -6, -5, -4: col = 45 case -3, -2, -1: col = 51 case 0, 1: col = 50 case 2, 3: col = 49 case 4, 5: col = 48 case 6, 7: col = 47 case 8, 9: col = 46 case 10, 11, 12: col = 82 case 13, 14, 15: col = 118 case 16, 17, 18: col = 154 case 19, 20, 21: col = 190 case 22, 23, 24: col = 226 case 25, 26, 27: col = 220 case 28, 29, 30: col = 214 case 31, 32, 33: col = 208 case 34, 35, 36: col = 202 default: if temp > 0 { col = 196 } } } else { col = 16 switch temp { case -15, -14, -13: col = 17 case -12, -11, -10: col = 18 case -9, -8, -7: col = 19 case -6, -5, -4: col = 20 case -3, -2, -1: col = 21 case 0, 1: col = 30 case 2, 3: col = 28 case 4, 5: col = 29 case 6, 7: col = 30 case 8, 9: col = 34 case 10, 11, 12: col = 35 case 13, 14, 15: col = 36 case 16, 17, 18: col = 40 case 19, 20, 21: col = 59 case 22, 23, 24: col = 100 case 25, 26, 27: col = 101 case 28, 29, 30: col = 94 case 31, 32, 33: col = 166 case 34, 35, 36: col = 52 default: if temp > 0 { col = 196 } } } if g.config.Imperial { temp = (temp*18 + 320) / 10 } if explicitPlus { return fmt.Sprintf("\033[38;5;%03dm+%d\033[0m", col, temp) } return fmt.Sprintf("\033[38;5;%03dm%d\033[0m", col, temp) } t := c.TempC if t == 0 { t = c.TempC2 } // hyphen := " - " // if (config.Lang == "sl") { // hyphen = "-" // } // hyphen = ".." explicitPlus1 := false explicitPlus2 := false if c.FeelsLikeC != t { if t > 0 { explicitPlus1 = true } if c.FeelsLikeC > 0 { explicitPlus2 = true } if explicitPlus1 { explicitPlus2 = false } return g.pad( fmt.Sprintf("%s(%s) °%s", color(t, explicitPlus1), color(c.FeelsLikeC, explicitPlus2), unitTemp()[g.config.Imperial]), 15) } return g.pad(fmt.Sprintf("%s °%s", color(c.FeelsLikeC, false), unitTemp()[g.config.Imperial]), 15) } func (g *global) formatWind(c cond) string { unitWindString := unitWind(0, g.config.Lang) if g.config.WindMS { unitWindString = unitWind(2, g.config.Lang) } else if g.config.Imperial { unitWindString = unitWind(1, g.config.Lang) } hyphen := "-" cWindGustKmph := speedToColor(c.WindGustKmph, windInRightUnits(c.WindGustKmph, g.config.WindMS, g.config.Imperial)) cWindspeedKmph := speedToColor(c.WindspeedKmph, windInRightUnits(c.WindspeedKmph, g.config.WindMS, g.config.Imperial)) if windInRightUnits(c.WindGustKmph, g.config.WindMS, g.config.Imperial) > windInRightUnits(c.WindspeedKmph, g.config.WindMS, g.config.Imperial) { return g.pad( fmt.Sprintf("%s %s%s%s %s", windDir()[c.Winddir16Point], cWindspeedKmph, hyphen, cWindGustKmph, unitWindString), 15) } return g.pad(fmt.Sprintf("%s %s %s", windDir()[c.Winddir16Point], cWindspeedKmph, unitWindString), 15) } func windInRightUnits(spd int, windMS, imperial bool) int { if windMS { spd = (spd * 1000) / 3600 } else if imperial { spd = (spd * 1000) / 1609 } return spd } func speedToColor(spd, spdConverted int) string { col := 46 switch spd { case 1, 2, 3: col = 82 case 4, 5, 6: col = 118 case 7, 8, 9: col = 154 case 10, 11, 12: col = 190 case 13, 14, 15: col = 226 case 16, 17, 18, 19: col = 220 case 20, 21, 22, 23: col = 214 case 24, 25, 26, 27: col = 208 case 28, 29, 30, 31: col = 202 default: if spd > 0 { col = 196 } } return fmt.Sprintf("\033[38;5;%03dm%d\033[0m", col, spdConverted) } func (g *global) formatVisibility(c cond) string { if g.config.Imperial { c.VisibleDistKM = (c.VisibleDistKM * 621) / 1000 } return g.pad(fmt.Sprintf("%d %s", c.VisibleDistKM, unitVis(g.config.Imperial, g.config.Lang)), 15) } func (g *global) formatRain(c cond) string { rainUnit := c.PrecipMM if g.config.Imperial { rainUnit = c.PrecipMM * 0.039 } if c.ChanceOfRain != "" { return g.pad(fmt.Sprintf( "%.1f %s | %s%%", rainUnit, unitRain(g.config.Imperial, g.config.Lang), c.ChanceOfRain), 15) } return g.pad(fmt.Sprintf("%.1f %s", rainUnit, unitRain(g.config.Imperial, g.config.Lang)), 15) } func (g *global) formatCond(cur []string, c cond, current bool) []string { var ( ret []string icon []string ) if i, ok := codes()[c.WeatherCode]; !ok { icon = getIcon("iconUnknown") } else { icon = i } if g.config.Inverse { // inverting colors for i := range icon { icon[i] = strings.ReplaceAll(icon[i], "38;5;226", "38;5;94") icon[i] = strings.ReplaceAll(icon[i], "38;5;250", "38;5;243") icon[i] = strings.ReplaceAll(icon[i], "38;5;21", "38;5;18") icon[i] = strings.ReplaceAll(icon[i], "38;5;255", "38;5;245") icon[i] = strings.ReplaceAll(icon[i], "38;5;111", "38;5;63") icon[i] = strings.ReplaceAll(icon[i], "38;5;251", "38;5;238") } } // desc := fmt.Sprintf("%-15.15v", c.WeatherDesc[0].Value) desc := c.WeatherDesc[0].Value if g.config.RightToLeft { for runewidth.StringWidth(desc) < 15 { desc = " " + desc } for runewidth.StringWidth(desc) > 15 { _, size := utf8.DecodeLastRuneInString(desc) desc = desc[size:] } } else { for runewidth.StringWidth(desc) < 15 { desc += " " } for runewidth.StringWidth(desc) > 15 { _, size := utf8.DecodeLastRuneInString(desc) desc = desc[:len(desc)-size] } } if current { if g.config.RightToLeft { desc = c.WeatherDesc[0].Value if runewidth.StringWidth(desc) < 15 { desc = strings.Repeat(" ", 15-runewidth.StringWidth(desc)) + desc } } else { desc = c.WeatherDesc[0].Value } } else { if g.config.RightToLeft { if frstRune, size := utf8.DecodeRuneInString(desc); frstRune != ' ' { desc = "…" + desc[size:] for runewidth.StringWidth(desc) < 15 { desc = " " + desc } } } else { if lastRune, size := utf8.DecodeLastRuneInString(desc); lastRune != ' ' { desc = desc[:len(desc)-size] + "…" // for numberOfSpaces < runewidth.StringWidth(fmt.Sprintf("%c", lastRune)) - 1 { for runewidth.StringWidth(desc) < 15 { desc += " " } } } } if g.config.RightToLeft { ret = append( ret, fmt.Sprintf("%v %v %v", cur[0], desc, icon[0]), fmt.Sprintf("%v %v %v", cur[1], g.formatTemp(c), icon[1]), fmt.Sprintf("%v %v %v", cur[2], g.formatWind(c), icon[2]), fmt.Sprintf("%v %v %v", cur[3], g.formatVisibility(c), icon[3]), fmt.Sprintf("%v %v %v", cur[4], g.formatRain(c), icon[4])) } else { ret = append( ret, fmt.Sprintf("%v %v %v", cur[0], icon[0], desc), fmt.Sprintf("%v %v %v", cur[1], icon[1], g.formatTemp(c)), fmt.Sprintf("%v %v %v", cur[2], icon[2], g.formatWind(c)), fmt.Sprintf("%v %v %v", cur[3], icon[3], g.formatVisibility(c)), fmt.Sprintf("%v %v %v", cur[4], icon[4], g.formatRain(c))) } return ret } func justifyCenter(s string, width int) string { appendSide := 0 for runewidth.StringWidth(s) <= width { if appendSide == 1 { s += " " appendSide = 0 } else { s = " " + s appendSide = 1 } } return s } func reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) } func (g *global) pad(s string, mustLen int) string { var ret string ret = s realLen := utf8.RuneCountInString(g.ansiEsc.ReplaceAllLiteralString(s, "")) delta := mustLen - realLen if delta > 0 { if g.config.RightToLeft { ret = strings.Repeat(" ", delta) + ret + "\033[0m" } else { ret += "\033[0m" + strings.Repeat(" ", delta) } } else if delta < 0 { toks := g.ansiEsc.Split(s, 2) tokLen := utf8.RuneCountInString(toks[0]) esc := g.ansiEsc.FindString(s) if tokLen > mustLen { ret = fmt.Sprintf("%.*s\033[0m", mustLen, toks[0]) } else { ret = fmt.Sprintf("%s%s%s", toks[0], esc, g.pad(toks[1], mustLen-tokLen)) } } return ret } ================================================ FILE: internal/view/v1/icons.go ================================================ package v1 //nolint:funlen func getIcon(name string) []string { icon := map[string][]string{ "iconUnknown": { " .-. ", " __) ", " ( ", " `-’ ", " • ", }, "iconSunny": { "\033[38;5;226m \\ / \033[0m", "\033[38;5;226m .-. \033[0m", "\033[38;5;226m ― ( ) ― \033[0m", "\033[38;5;226m `-’ \033[0m", "\033[38;5;226m / \\ \033[0m", }, "iconPartlyCloudy": { "\033[38;5;226m \\ /\033[0m ", "\033[38;5;226m _ /\"\"\033[38;5;250m.-. \033[0m", "\033[38;5;226m \\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", " ", }, "iconCloudy": { " ", "\033[38;5;250m .--. \033[0m", "\033[38;5;250m .-( ). \033[0m", "\033[38;5;250m (___.__)__) \033[0m", " ", }, "iconVeryCloudy": { " ", "\033[38;5;240;1m .--. \033[0m", "\033[38;5;240;1m .-( ). \033[0m", "\033[38;5;240;1m (___.__)__) \033[0m", " ", }, "iconLightShowers": { "\033[38;5;226m _`/\"\"\033[38;5;250m.-. \033[0m", "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", }, "iconHeavyShowers": { "\033[38;5;226m _`/\"\"\033[38;5;240;1m.-. \033[0m", "\033[38;5;226m ,\\_\033[38;5;240;1m( ). \033[0m", "\033[38;5;226m /\033[38;5;240;1m(___(__) \033[0m", "\033[38;5;21;1m ‚‘‚‘‚‘‚‘ \033[0m", "\033[38;5;21;1m ‚’‚’‚’‚’ \033[0m", }, "iconLightSnowShowers": { "\033[38;5;226m _`/\"\"\033[38;5;250m.-. \033[0m", "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;255m * * * \033[0m", "\033[38;5;255m * * * \033[0m", }, "iconHeavySnowShowers": { "\033[38;5;226m _`/\"\"\033[38;5;240;1m.-. \033[0m", "\033[38;5;226m ,\\_\033[38;5;240;1m( ). \033[0m", "\033[38;5;226m /\033[38;5;240;1m(___(__) \033[0m", "\033[38;5;255;1m * * * * \033[0m", "\033[38;5;255;1m * * * * \033[0m", }, "iconLightSleetShowers": { "\033[38;5;226m _`/\"\"\033[38;5;250m.-. \033[0m", "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;111m ‘ \033[38;5;255m*\033[38;5;111m ‘ \033[38;5;255m* \033[0m", "\033[38;5;255m *\033[38;5;111m ‘ \033[38;5;255m*\033[38;5;111m ‘ \033[0m", }, "iconThunderyShowers": { "\033[38;5;226m _`/\"\"\033[38;5;250m.-. \033[0m", "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;228;5m ⚡\033[38;5;111;25m‘‘\033[38;5;228;5m⚡\033[38;5;111;25m‘‘ \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", }, "iconThunderyHeavyRain": { "\033[38;5;240;1m .-. \033[0m", "\033[38;5;240;1m ( ). \033[0m", "\033[38;5;240;1m (___(__) \033[0m", "\033[38;5;21;1m ‚‘\033[38;5;228;5m⚡\033[38;5;21;25m‘‚\033[38;5;228;5m⚡\033[38;5;21;25m‚‘ \033[0m", "\033[38;5;21;1m ‚’‚’\033[38;5;228;5m⚡\033[38;5;21;25m’‚’ \033[0m", }, "iconThunderySnowShowers": { "\033[38;5;226m _`/\"\"\033[38;5;250m.-. \033[0m", "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;255m *\033[38;5;228;5m⚡\033[38;5;255;25m*\033[38;5;228;5m⚡\033[38;5;255;25m* \033[0m", "\033[38;5;255m * * * \033[0m", }, "iconLightRain": { "\033[38;5;250m .-. \033[0m", "\033[38;5;250m ( ). \033[0m", "\033[38;5;250m (___(__) \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", }, "iconHeavyRain": { "\033[38;5;240;1m .-. \033[0m", "\033[38;5;240;1m ( ). \033[0m", "\033[38;5;240;1m (___(__) \033[0m", "\033[38;5;21;1m ‚‘‚‘‚‘‚‘ \033[0m", "\033[38;5;21;1m ‚’‚’‚’‚’ \033[0m", }, "iconLightSnow": { "\033[38;5;250m .-. \033[0m", "\033[38;5;250m ( ). \033[0m", "\033[38;5;250m (___(__) \033[0m", "\033[38;5;255m * * * \033[0m", "\033[38;5;255m * * * \033[0m", }, "iconHeavySnow": { "\033[38;5;240;1m .-. \033[0m", "\033[38;5;240;1m ( ). \033[0m", "\033[38;5;240;1m (___(__) \033[0m", "\033[38;5;255;1m * * * * \033[0m", "\033[38;5;255;1m * * * * \033[0m", }, "iconLightSleet": { "\033[38;5;250m .-. \033[0m", "\033[38;5;250m ( ). \033[0m", "\033[38;5;250m (___(__) \033[0m", "\033[38;5;111m ‘ \033[38;5;255m*\033[38;5;111m ‘ \033[38;5;255m* \033[0m", "\033[38;5;255m *\033[38;5;111m ‘ \033[38;5;255m*\033[38;5;111m ‘ \033[0m", }, "iconFog": { " ", "\033[38;5;251m _ - _ - _ - \033[0m", "\033[38;5;251m _ - _ - _ \033[0m", "\033[38;5;251m _ - _ - _ - \033[0m", " ", }, } return icon[name] } func codes() map[int][]string { return map[int][]string{ 113: getIcon("iconSunny"), 116: getIcon("iconPartlyCloudy"), 119: getIcon("iconCloudy"), 122: getIcon("iconVeryCloudy"), 143: getIcon("iconFog"), 176: getIcon("iconLightShowers"), 179: getIcon("iconLightSleetShowers"), 182: getIcon("iconLightSleet"), 185: getIcon("iconLightSleet"), 200: getIcon("iconThunderyShowers"), 227: getIcon("iconLightSnow"), 230: getIcon("iconHeavySnow"), 248: getIcon("iconFog"), 260: getIcon("iconFog"), 263: getIcon("iconLightShowers"), 266: getIcon("iconLightRain"), 281: getIcon("iconLightSleet"), 284: getIcon("iconLightSleet"), 293: getIcon("iconLightRain"), 296: getIcon("iconLightRain"), 299: getIcon("iconHeavyShowers"), 302: getIcon("iconHeavyRain"), 305: getIcon("iconHeavyShowers"), 308: getIcon("iconHeavyRain"), 311: getIcon("iconLightSleet"), 314: getIcon("iconLightSleet"), 317: getIcon("iconLightSleet"), 320: getIcon("iconLightSnow"), 323: getIcon("iconLightSnowShowers"), 326: getIcon("iconLightSnowShowers"), 329: getIcon("iconHeavySnow"), 332: getIcon("iconHeavySnow"), 335: getIcon("iconHeavySnowShowers"), 338: getIcon("iconHeavySnow"), 350: getIcon("iconLightSleet"), 353: getIcon("iconLightShowers"), 356: getIcon("iconHeavyShowers"), 359: getIcon("iconHeavyRain"), 362: getIcon("iconLightSleetShowers"), 365: getIcon("iconLightSleetShowers"), 368: getIcon("iconLightSnowShowers"), 371: getIcon("iconHeavySnowShowers"), 374: getIcon("iconLightSleetShowers"), 377: getIcon("iconLightSleet"), 386: getIcon("iconThunderyShowers"), 389: getIcon("iconThunderyHeavyRain"), 392: getIcon("iconThunderySnowShowers"), 395: getIcon("iconHeavySnowShowers"), } } ================================================ FILE: internal/view/v1/locale.go ================================================ package v1 //nolint:funlen func locale() map[string]string { return map[string]string{ "af": "af_ZA", "am": "am_ET", "ar": "ar_TN", "az": "az_AZ", "be": "be_BY", "bg": "bg_BG", "bn": "bn_IN", "bs": "bs_BA", "ca": "ca_ES", "cs": "cs_CZ", "cy": "cy_GB", "da": "da_DK", "de": "de_DE", "el": "el_GR", "eo": "eo", "es": "es_ES", "et": "et_EE", "eu": "eu_ES", "fa": "fa_IR", "fi": "fi_FI", "fr": "fr_FR", "fy": "fy_NL", "ga": "ga_IE", "gl": "gl_ES", "he": "he_IL", "hi": "hi_IN", "hr": "hr_HR", "hu": "hu_HU", "hy": "hy_AM", "ia": "ia", "id": "id_ID", "is": "is_IS", "it": "it_IT", "ja": "ja_JP", "jv": "en_US", "ka": "ka_GE", "kk": "kk_KZ", "ko": "ko_KR", "ky": "ky_KG", "lt": "lt_LT", "lv": "lv_LV", "mg": "mg_MG", "mk": "mk_MK", "ml": "ml_IN", "mr": "mr_IN", "nb": "nb_NO", "nl": "nl_NL", "nn": "nn_NO", "oc": "oc_FR", "pl": "pl_PL", "pt-br": "pt_BR", "pt": "pt_PT", "ro": "ro_RO", "ru": "ru_RU", "sk": "sk_SK", "sl": "sl_SI", "sr-lat": "sr_RS@latin", "sr": "sr_RS", "sv": "sv_SE", "sw": "sw_KE", "ta": "ta_IN", "th": "th_TH", "tr": "tr_TR", "uk": "uk_UA", "uz": "uz_UZ", "vi": "vi_VN", "zh-cn": "zh_CN", "zh-tw": "zh_TW", "zh": "zh_CN", "zu": "zu_ZA", } } //nolint:funlen func localizedCaption() map[string]string { return map[string]string{ "af": "Weer verslag vir:", "am": "የአየር ሁኔታ ዘገባ ለ ፥", "ar": "تقرير حالة ألطقس", "az": "Hava proqnozu:", "be": "Прагноз надвор'я для:", "bg": "Прогноза за времето в:", "bn": "আবহাওয়া সঙ্ক্রান্ত তথ্য", "bs": "Vremenske prognoze za:", "ca": "Informe del temps per a:", "cs": "Předpověď počasí pro:", "cy": "Adroddiad tywydd ar gyfer:", "da": "Vejret i:", "de": "Wetterbericht für:", "el": "Πρόγνωση καιρού για:", "eo": "Veterprognozo por:", "es": "El tiempo en:", "et": "Ilmaprognoos:", "eu": "Eguraldia:", "fa": "اوه و بآ تیعضو شرازگ", "fi": "Säätiedotus:", "fr": "Prévisions météo pour:", "fy": "Waarberjocht foar:", "ga": "Réamhaisnéis na haimsire do:", "gl": "Previsión do tempo en:", "he": ":ריוואה גזמ תיזחת", "hi": "मौसम की जानकारी", "hr": "Vremenska prognoza za:", "hu": "Időjárás előrejelzés:", "hy": "Եղանակի տեսություն:", "ia": "Le tempore a:", "id": "Prakiraan cuaca:", "it": "Previsioni meteo:", "is": "Veðurskýrsla fyrir:", "ja": "天気予報:", "jv": "Weather forecast for:", "ka": "ამინდის პროგნოზი:", "kk": "Ауа райы:", "ko": "일기 예보:", "ky": "Аба ырайы:", "lt": "Orų prognozė:", "lv": "Laika ziņas:", "mk": "Прогноза за времето во:", "ml": "കാലാവസ്ഥ റിപ്പോർട്ട്:", "mr": "हवामानाचा अंदाज:", "nb": "Værmelding for:", "nl": "Weerbericht voor:", "nn": "Vêrmelding for:", "oc": "Previsions metèo per:", "pl": "Pogoda w:", "pt": "Previsão do tempo para:", "pt-br": "Previsão do tempo para:", "ro": "Prognoza meteo pentru:", "ru": "Прогноз погоды:", "sk": "Predpoveď počasia pre:", "sl": "Vremenska napoved za", "sr": "Временска прогноза за:", "sr-lat": "Vremenska prognoza za:", "sv": "Väderleksprognos för:", "sw": "Ripoti ya hali ya hewa, jiji la:", "ta": "வானிலை அறிக்கை", "te": "వాతావరణ సమాచారము:", "th": "รายงานสภาพอากาศ:", "tr": "Hava beklentisi:", "uk": "Прогноз погоди для:", "uz": "Ob-havo bashorati:", "vi": "Báo cáo thời tiết:", "zu": "Isimo sezulu:", "zh": "天气预报:", "zh-cn": "天气预报:", "zh-tw": "天氣預報:", "mg": "Vinavina toetr'andro hoan'ny:", } } //nolint:misspell,funlen func daytimeTranslation() map[string][]string { return map[string][]string{ "af": {"Oggend", "Middag", "Vroegaand", "Laatnag"}, "am": {"ጠዋት", "ከሰዓት በኋላ", "ምሽት", "ሌሊት"}, "ar": {"ﺎﻠﻠﻴﻟ", "ﺎﻠﻤﺳﺍﺀ", "ﺎﻠﻈﻫﺭ", "ﺎﻠﺼﺑﺎﺣ"}, "az": {"Səhər", "Gün", "Axşam", "Gecə"}, "be": {"Раніца", "Дзень", "Вечар", "Ноч"}, "bg": {"Сутрин", "Обяд", "Вечер", "Нощ"}, "bn": {"সকাল", "দুপুর", "সন্ধ্যা", "রাত্রি"}, "bs": {"Ujutro", "Dan", "Večer", "Noć"}, "cs": {"Ráno", "Ve dne", "Večer", "V noci"}, "ca": {"Matí", "Dia", "Tarda", "Nit"}, "cy": {"Bore", "Dydd", "Hwyr", "Nos"}, "da": {"Morgen", "Middag", "Aften", "Nat"}, "de": {"Morgen", "Mittag", "Abend", "Nacht"}, "el": {"Πρωί", "Μεσημέρι", "Απόγευμα", "Βράδυ"}, "en": {"Morning", "Noon", "Evening", "Night"}, "eo": {"Mateno", "Tago", "Vespero", "Nokto"}, "es": {"Mañana", "Mediodía", "Tarde", "Noche"}, "et": {"Hommik", "Päev", "Õhtu", "Öösel"}, "eu": {"Goiza", "Eguerdia", "Arratsaldea", "Gaua"}, "fa": {"حبص", "رهظ", "رصع", "بش"}, "fi": {"Aamu", "Keskipäivä", "Ilta", "Yö"}, "fr": {"Matin", "Après-midi", "Soir", "Nuit"}, "fy": {"Moarns", "Middeis", "Jûns", "Nachts"}, "ga": {"Maidin", "Nóin", "Tráthnóna", "Oíche"}, "gl": {"Mañá", "Mediodía", "Tarde", "Noite"}, "he": {"רקוב", "םוֹיְ", "ברֶעֶ", "הלָיְלַ"}, "hi": {"प्रातःकाल", "दोपहर", "सायंकाल", "रात"}, "hr": {"Jutro", "Dan", "Večer", "Noć"}, "hu": {"Reggel", "Dél", "Este", "Éjszaka"}, "hy": {"Առավոտ", "Կեսօր", "Երեկո", "Գիշեր"}, "ia": {"Matino", "Mediedie", "Vespere", "Nocte"}, "id": {"Pagi", "Hari", "Petang", "Malam"}, "it": {"Mattina", "Pomeriggio", "Sera", "Notte"}, "is": {"Morgunn", "Dagur", "Kvöld", "Nótt"}, "ja": {"朝", "昼", "夕", "夜"}, "jv": {"Morning", "Noon", "Evening", "Night"}, "ka": {"დილა", "დღე", "საღამო", "ღამე"}, "kk": {"Таң", "Күндіз", "Кеш", "Түн"}, "ko": {"아침", "낮", "저녁", "밤"}, "ky": {"Эртең", "Күн", "Кеч", "Түн"}, "lt": {"Rytas", "Diena", "Vakaras", "Naktis"}, "lv": {"Rīts", "Diena", "Vakars", "Nakts"}, "mk": {"Утро", "Пладне", "Вечер", "Ноќ"}, "ml": {"രാവിലെ", "മധ്യാഹ്നം", "വൈകുന്നേരം", "രാത്രി"}, "mr": {"सकाळ", "दुपार", "संध्याकाळ", "रात्र"}, "nl": {"'s Ochtends", "'s Middags", "'s Avonds", "'s Nachts"}, "nb": {"Morgen", "Middag", "Kveld", "Natt"}, "nn": {"Morgon", "Middag", "Kveld", "Natt"}, "oc": {"Matin", "Jorn", "Vèspre", "Nuèch"}, "pl": {"Ranek", "Dzień", "Wieczór", "Noc"}, "pt": {"Manhã", "Meio-dia", "Tarde", "Noite"}, "pt-br": {"Manhã", "Meio-dia", "Tarde", "Noite"}, "ro": {"Dimineaţă", "Amiază", "Seară", "Noapte"}, "ru": {"Утро", "День", "Вечер", "Ночь"}, "sk": {"Ráno", "Cez deň", "Večer", "V noci"}, "sl": {"Jutro", "Dan", "Večer", "Noč"}, "sr": {"Јутро", "Подне", "Вече", "Ноћ"}, "sr-lat": {"Jutro", "Podne", "Veče", "Noć"}, "sv": {"Morgon", "Eftermiddag", "Kväll", "Natt"}, "sw": {"Asubuhi", "Adhuhuri", "Jioni", "Usiku"}, "ta": {"காலை", "நண்பகல்", "சாயங்காலம்", "இரவு"}, "te": {"ఉదయం", "రోజు", "సాయంత్రం", "రాత్రి"}, "th": {"เช้า", "วัน", "เย็น", "คืน"}, "tr": {"Sabah", "Öğle", "Akşam", "Gece"}, "uk": {"Ранок", "День", "Вечір", "Ніч"}, "uz": {"Ertalab", "Kunduzi", "Kechqurun", "Kecha"}, "vi": {"Sáng", "Trưa", "Chiều", "Tối"}, "zh": {"早上", "中午", "傍晚", "夜间"}, "zh-cn": {"早上", "中午", "傍晚", "夜间"}, "zh-tw": {"早上", "中午", "傍晚", "夜間"}, "zu": {"Morning", "Noon", "Evening", "Night"}, "mg": {"Maraina", "Tolakandro", "Ariva", "Alina"}, } } func unitTemp() map[bool]string { return map[bool]string{ false: "C", true: "F", } } func localizedRain() map[string]map[bool]string { return map[string]map[bool]string{ "en": { false: "mm", true: "in", }, "be": { false: "мм", true: "in", }, "ru": { false: "мм", true: "in", }, "uk": { false: "мм", true: "in", }, } } func localizedVis() map[string]map[bool]string { return map[string]map[bool]string{ "en": { false: "km", true: "mi", }, "be": { false: "км", true: "mi", }, "ru": { false: "км", true: "mi", }, "uk": { false: "км", true: "mi", }, } } func localizedWind() map[string]map[int]string { return map[string]map[int]string{ "en": { 0: "km/h", 1: "mph", 2: "m/s", }, "be": { 0: "км/г", 1: "mph", 2: "м/c", }, "ru": { 0: "км/ч", 1: "mph", 2: "м/c", }, "tr": { 0: "km/sa", 1: "mph", 2: "m/s", }, "uk": { 0: "км/год", 1: "mph", 2: "м/c", }, } } func unitWind(unit int, lang string) string { translation, ok := localizedWind()[lang] if !ok { translation = localizedWind()["en"] } return translation[unit] } func unitVis(unit bool, lang string) string { translation, ok := localizedVis()[lang] if !ok { translation = localizedVis()["en"] } return translation[unit] } func unitRain(unit bool, lang string) string { translation, ok := localizedRain()[lang] if !ok { translation = localizedRain()["en"] } return translation[unit] } ================================================ FILE: internal/view/v1/view1.go ================================================ package v1 import ( "math" "time" "github.com/klauspost/lctime" ) func slotTimes() []int { return []int{9 * 60, 12 * 60, 18 * 60, 22 * 60} } //nolint:funlen,gocognit,cyclop func (g *global) printDay(w weather) ([]string, error) { var ( ret = []string{} dateName string names string ) hourly := w.Hourly for i := range ret { ret[i] = "│" } // find hourly data which fits the desired times of day best var slots [slotcount]cond for _, h := range hourly { c := int(math.Mod(float64(h.Time), 100)) + 60*(h.Time/100) for i, s := range slots { if math.Abs(float64(c-slotTimes()[i])) < math.Abs(float64(s.Time-slotTimes()[i])) { h.Time = c slots[i] = h } } } if g.config.RightToLeft { slots[0], slots[3] = slots[3], slots[0] slots[1], slots[2] = slots[2], slots[1] } for i, s := range slots { if g.config.Narrow { if i == 0 || i == 2 { continue } } ret = g.formatCond(ret, s, false) for i := range ret { ret[i] += "│" } } d, _ := time.Parse("2006-01-02", w.Date) // dateFmt := "┤ " + d.Format("Mon 02. Jan") + " ├" if val, ok := locale()[g.config.Lang]; ok { err := lctime.SetLocale(val) if err != nil { return nil, err } } else { err := lctime.SetLocale("en_US") if err != nil { return nil, err } } if g.config.RightToLeft { dow := lctime.Strftime("%a", d) day := lctime.Strftime("%d", d) month := lctime.Strftime("%b", d) dateName = reverse(month) + " " + day + " " + reverse(dow) } else { switch g.config.Lang { case "ko": date_format = "%b %d일 %a" case "lv": date_format = "%a., %d. %b." case "zh", "zh-cn", "zh-tw": date_format = "%b%d日%A" default: date_format = "%a %d %b" } dateName = lctime.Strftime(date_format, d) } dateFmt := "┤" + justifyCenter(dateName, 12) + "├" trans := daytimeTranslation()["en"] if t, ok := daytimeTranslation()[g.config.Lang]; ok { trans = t } if g.config.Narrow { names := "│ " + justifyCenter(trans[1], 16) + "└──────┬──────┘" + justifyCenter(trans[3], 16) + " │" ret = append([]string{ " ┌─────────────┐ ", "┌───────────────────────" + dateFmt + "───────────────────────┐", names, "├──────────────────────────────┼──────────────────────────────┤", }, ret...) return append(ret, "└──────────────────────────────┴──────────────────────────────┘"), nil } if g.config.RightToLeft { names = "│" + justifyCenter(trans[3], 29) + "│ " + justifyCenter(trans[2], 16) + "└──────┬──────┘" + justifyCenter(trans[1], 16) + " │" + justifyCenter(trans[0], 29) + "│" } else { names = "│" + justifyCenter(trans[0], 29) + "│ " + justifyCenter(trans[1], 16) + "└──────┬──────┘" + justifyCenter(trans[2], 16) + " │" + justifyCenter(trans[3], 29) + "│" } //nolint:lll ret = append([]string{ " ┌─────────────┐ ", "┌──────────────────────────────┬───────────────────────" + dateFmt + "───────────────────────┬──────────────────────────────┐", names, "├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤", }, ret...) //nolint:lll return append(ret, "└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘"), nil } ================================================ FILE: lib/airports.py ================================================ import csv AIRPORTS_DAT_FILE = "/home/igor/wttrin-geo/share/airports.dat" def load_aiports_index(): file_ = open(AIRPORTS_DAT_FILE, "r") reader = csv.reader(file_) airport_index = {} for line in reader: airport_index[line[4]] = line return airport_index AIRPORTS_INDEX = load_aiports_index() def get_airport_gps_location(iata_code): if iata_code in AIRPORTS_INDEX: airport = AIRPORTS_INDEX[iata_code] return "%s,%s airport" % (airport[6], airport[7]) # , airport[1]) return None ================================================ FILE: lib/buttons.py ================================================ TWITTER_BUTTON = """ """ GITHUB_BUTTON = """ wttr.in """ GITHUB_BUTTON_2 = """ wego """ GITHUB_BUTTON_3 = """ pyphoon """ GITHUB_BUTTON_FOOTER = """ """ def add_buttons(output): """ Add buttons to html output """ return output.replace( "", ( TWITTER_BUTTON + GITHUB_BUTTON + GITHUB_BUTTON_3 + GITHUB_BUTTON_2 + GITHUB_BUTTON_FOOTER ) + "", ) ================================================ FILE: lib/cache.py ================================================ """ LRU-Cache implementation for formatted (`format=`) answers """ import datetime import re import time import os import hashlib import random import pytz import pylru from globals import LRU_CACHE CACHE_SIZE = 10000 CACHE = pylru.lrucache(CACHE_SIZE) # strings longer than this are stored not in ram # but in the file cache MIN_SIZE_FOR_FILECACHE = 80 def _update_answer(answer): def _now_in_tz(timezone): return datetime.datetime.now(pytz.timezone(timezone)).strftime("%H:%M:%S%z") if isinstance(answer, str) and "%{{NOW(" in answer: answer = re.sub( r"%{{NOW\(([^}]*)\)}}", lambda x: _now_in_tz(x.group(1)), answer ) return answer def get_signature(user_agent, query_string, client_ip_address, lang): """ Get cache signature based on `user_agent`, `url_string`, `lang`, and `client_ip_address` Return `None` if query should not be cached. """ if "?" in query_string: location = query_string.split("?", 1)[0] else: location = query_string if location.startswith("http://"): location = location[7:] elif location.startswith("https://"): location = location[8:] if ":" in location: return None signature = "%s:%s:%s:%s" % (user_agent, query_string, client_ip_address, lang) print(signature) return signature def get(signature): """ If `update_answer` is not True, return answer as it is stored in the cache. Otherwise update it, using the `_update_answer` function. """ if not signature: return None value_record = CACHE.get(signature) if not value_record: return None value = value_record["val"] expiry = value_record["expiry"] if value and time.time() < expiry: if value.startswith("file:") or value.startswith("bfile:"): value = _read_from_file(signature, sighash=value) if not value: return None return _update_answer(value) return None def _randint(minimum, maximum): return random.randrange(maximum - minimum) def store(signature, value): """ Store in cache `value` for `signature` """ if not signature: return _update_answer(value) if len(value) >= MIN_SIZE_FOR_FILECACHE: value_to_store = _store_in_file(signature, value) else: value_to_store = value value_record = { "val": value_to_store, "expiry": time.time() + _randint(1000, 2000), } CACHE[signature] = value_record return _update_answer(value) def _hash(signature): return hashlib.md5(signature.encode("utf-8")).hexdigest() def _store_in_file(signature, value): """Store `value` for `signature` in cache file. Return file name (signature_hash) as the result. `value` can be string as well as bytes. Returned filename is prefixed with "file:" (for text files) or "bfile:" (for binary files). """ signature_hash = _hash(signature) filename = os.path.join(LRU_CACHE, signature_hash) if not os.path.exists(LRU_CACHE): os.makedirs(LRU_CACHE) if isinstance(value, bytes): mode = "wb" signature_hash = "bfile:%s" % signature_hash else: mode = "w" signature_hash = "file:%s" % signature_hash with open(filename, mode) as f_cache: f_cache.write(value) return signature_hash def _read_from_file(signature, sighash=None): """Read value for `signature` from cache file, or return None if file is not found. If `sighash` is specified, do not calculate file name from signature, but use `sighash` instead. `sigash` can be prefixed with "file:" (for text files) or "bfile:" (for binary files). """ mode = "r" if sighash: if sighash.startswith("file:"): sighash = sighash[5:] elif sighash.startswith("bfile:"): sighash = sighash[6:] mode = "rb" else: sighash = _hash(signature) filename = os.path.join(LRU_CACHE, sighash) if not os.path.exists(filename): return None with open(filename, mode) as f_cache: return f_cache.read() ================================================ FILE: lib/constants.py ================================================ # vim: fileencoding=utf-8 WWO_CODE = { "113": "Sunny", "116": "PartlyCloudy", "119": "Cloudy", "122": "VeryCloudy", "143": "Fog", "176": "LightShowers", "179": "LightSleetShowers", "182": "LightSleet", "185": "LightSleet", "200": "ThunderyShowers", "227": "LightSnow", "230": "HeavySnow", "248": "Fog", "260": "Fog", "263": "LightShowers", "266": "LightRain", "281": "LightSleet", "284": "LightSleet", "293": "LightRain", "296": "LightRain", "299": "HeavyShowers", "302": "HeavyRain", "305": "HeavyShowers", "308": "HeavyRain", "311": "LightSleet", "314": "LightSleet", "317": "LightSleet", "320": "LightSnow", "323": "LightSnowShowers", "326": "LightSnowShowers", "329": "HeavySnow", "332": "HeavySnow", "335": "HeavySnowShowers", "338": "HeavySnow", "350": "LightSleet", "353": "LightShowers", "356": "HeavyShowers", "359": "HeavyRain", "362": "LightSleetShowers", "365": "LightSleetShowers", "368": "LightSnowShowers", "371": "HeavySnowShowers", "374": "LightSleetShowers", "377": "LightSleet", "386": "ThunderyShowers", "389": "ThunderyHeavyRain", "392": "ThunderySnowShowers", "395": "HeavySnowShowers", } WEATHER_SYMBOL = { "Unknown": "✨", "Cloudy": "☁️", "Fog": "🌫", "HeavyRain": "🌧", "HeavyShowers": "🌧", "HeavySnow": "❄️", "HeavySnowShowers": "❄️", "LightRain": "🌦", "LightShowers": "🌦", "LightSleet": "🌧", "LightSleetShowers": "🌧", "LightSnow": "🌨", "LightSnowShowers": "🌨", "PartlyCloudy": "⛅️", "Sunny": "☀️", "ThunderyHeavyRain": "🌩", "ThunderyShowers": "⛈", "ThunderySnowShowers": "⛈", "VeryCloudy": "☁️", } WEATHER_SYMBOL_WIDTH_VTE = { "✨": 2, "☁️": 1, "🌫": 2, "🌧": 2, "🌧": 2, "❄️": 1, "❄️": 1, "🌦": 1, "🌦": 1, "🌧": 1, "🌧": 1, "🌨": 2, "🌨": 2, "⛅️": 2, "☀️": 1, "🌩": 2, "⛈": 1, "⛈": 1, "☁️": 1, } WIND_DIRECTION = [ "↓", "↙", "←", "↖", "↑", "↗", "→", "↘", ] MOON_PHASES = ("🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘") WEATHER_SYMBOL_WI_DAY = { "Unknown": "", "Cloudy": "", "Fog": "", "HeavyRain": "", "HeavyShowers": "", "HeavySnow": "", "HeavySnowShowers": "", "LightRain": "", "LightShowers": "", "LightSleet": "", "LightSleetShowers": "", "LightSnow": "", "LightSnowShowers": "", "PartlyCloudy": "", "Sunny": "", "ThunderyHeavyRain": "", "ThunderyShowers": "", "ThunderySnowShowers": "", "VeryCloudy": "", } WEATHER_SYMBOL_WI_NIGHT = { "Unknown": "", "Cloudy": "", "Fog": "", "HeavyRain": "", "HeavyShowers": "", "HeavySnow": "", "HeavySnowShowers": "", "LightRain": "", "LightShowers": "", "LightSleet": "", "LightSleetShowers": "", "LightSnow": "", "LightSnowShowers": "", "PartlyCloudy": "", "Sunny": "", "ThunderyHeavyRain": "", "ThunderyShowers": "", "ThunderySnowShowers": "", "VeryCloudy": "", } WEATHER_SYMBOL_PLAIN = { "Unknown": "?", "Cloudy": "mm", "Fog": "=", "HeavyRain": "///", "HeavyShowers": "//", "HeavySnow": "**", "HeavySnowShowers": "*/*", "LightRain": "/", "LightShowers": ".", "LightSleet": "x", "LightSleetShowers": "x/", "LightSnow": "*", "LightSnowShowers": "*/", "PartlyCloudy": "m", "Sunny": "o", "ThunderyHeavyRain": "/!/", "ThunderyShowers": "!/", "ThunderySnowShowers": "*!*", "VeryCloudy": "mmm", } WEATHER_SYMBOL_WIDTH_VTE_WI = {} WIND_DIRECTION_WI = [ "", "", "", "", "", "", "", "", ] WIND_SCALE_WI = [ "", "", "", "", "", "", "", "", "", "", "", "", "", ] MOON_PHASES_WI = ( "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ) WEATHER_SYMBOL_WEGO = { "Unknown": [ " .-. ", " __) ", " ( ", " `-’ ", " • ", ], "Sunny": [ "\033[38;5;226m \\ / \033[0m", "\033[38;5;226m .-. \033[0m", "\033[38;5;226m ― ( ) ― \033[0m", "\033[38;5;226m `-’ \033[0m", "\033[38;5;226m / \\ \033[0m", ], "PartlyCloudy": [ "\033[38;5;226m \\ /\033[0m ", '\033[38;5;226m _ /""\033[38;5;250m.-. \033[0m', "\033[38;5;226m \\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", " ", ], "Cloudy": [ " ", "\033[38;5;250m .--. \033[0m", "\033[38;5;250m .-( ). \033[0m", "\033[38;5;250m (___.__)__) \033[0m", " ", ], "VeryCloudy": [ " ", "\033[38;5;240;1m .--. \033[0m", "\033[38;5;240;1m .-( ). \033[0m", "\033[38;5;240;1m (___.__)__) \033[0m", " ", ], "LightShowers": [ '\033[38;5;226m _`/""\033[38;5;250m.-. \033[0m', "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", ], "HeavyShowers": [ '\033[38;5;226m _`/""\033[38;5;240;1m.-. \033[0m', "\033[38;5;226m ,\\_\033[38;5;240;1m( ). \033[0m", "\033[38;5;226m /\033[38;5;240;1m(___(__) \033[0m", "\033[38;5;21;1m ‚‘‚‘‚‘‚‘ \033[0m", "\033[38;5;21;1m ‚’‚’‚’‚’ \033[0m", ], "LightSnowShowers": [ '\033[38;5;226m _`/""\033[38;5;250m.-. \033[0m', "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;255m * * * \033[0m", "\033[38;5;255m * * * \033[0m", ], "HeavySnowShowers": [ '\033[38;5;226m _`/""\033[38;5;240;1m.-. \033[0m', "\033[38;5;226m ,\\_\033[38;5;240;1m( ). \033[0m", "\033[38;5;226m /\033[38;5;240;1m(___(__) \033[0m", "\033[38;5;255;1m * * * * \033[0m", "\033[38;5;255;1m * * * * \033[0m", ], "LightSleetShowers": [ '\033[38;5;226m _`/""\033[38;5;250m.-. \033[0m', "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;111m ‘ \033[38;5;255m*\033[38;5;111m ‘ \033[38;5;255m* \033[0m", "\033[38;5;255m *\033[38;5;111m ‘ \033[38;5;255m*\033[38;5;111m ‘ \033[0m", ], "ThunderyShowers": [ '\033[38;5;226m _`/""\033[38;5;250m.-. \033[0m', "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;228;5m ⚡\033[38;5;111;25m‘ ‘\033[38;5;228;5m⚡\033[38;5;111;25m‘ ‘ \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", ], "ThunderyHeavyRain": [ "\033[38;5;240;1m .-. \033[0m", "\033[38;5;240;1m ( ). \033[0m", "\033[38;5;240;1m (___(__) \033[0m", "\033[38;5;21;1m ‚‘\033[38;5;228;5m⚡\033[38;5;21;25m‘‚\033[38;5;228;5m⚡\033[38;5;21;25m‚‘ \033[0m", "\033[38;5;21;1m ‚’‚’\033[38;5;228;5m⚡\033[38;5;21;25m’‚’ \033[0m", ], "ThunderySnowShowers": [ '\033[38;5;226m _`/""\033[38;5;250m.-. \033[0m', "\033[38;5;226m ,\\_\033[38;5;250m( ). \033[0m", "\033[38;5;226m /\033[38;5;250m(___(__) \033[0m", "\033[38;5;255m *\033[38;5;228;5m⚡\033[38;5;255;25m*\033[38;5;228;5m⚡\033[38;5;255;25m* \033[0m", "\033[38;5;255m * * * \033[0m", ], "LightRain": [ "\033[38;5;250m .-. \033[0m", "\033[38;5;250m ( ). \033[0m", "\033[38;5;250m (___(__) \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", "\033[38;5;111m ‘ ‘ ‘ ‘ \033[0m", ], "HeavyRain": [ "\033[38;5;240;1m .-. \033[0m", "\033[38;5;240;1m ( ). \033[0m", "\033[38;5;240;1m (___(__) \033[0m", "\033[38;5;21;1m ‚‘‚‘‚‘‚‘ \033[0m", "\033[38;5;21;1m ‚’‚’‚’‚’ \033[0m", ], "LightSnow": [ "\033[38;5;250m .-. \033[0m", "\033[38;5;250m ( ). \033[0m", "\033[38;5;250m (___(__) \033[0m", "\033[38;5;255m * * * \033[0m", "\033[38;5;255m * * * \033[0m", ], "HeavySnow": [ "\033[38;5;240;1m .-. \033[0m", "\033[38;5;240;1m ( ). \033[0m", "\033[38;5;240;1m (___(__) \033[0m", "\033[38;5;255;1m * * * * \033[0m", "\033[38;5;255;1m * * * * \033[0m", ], "LightSleet": [ "\033[38;5;250m .-. \033[0m", "\033[38;5;250m ( ). \033[0m", "\033[38;5;250m (___(__) \033[0m", "\033[38;5;111m ‘ \033[38;5;255m*\033[38;5;111m ‘ \033[38;5;255m* \033[0m", "\033[38;5;255m *\033[38;5;111m ‘ \033[38;5;255m*\033[38;5;111m ‘ \033[0m", ], "Fog": [ " ", "\033[38;5;251m _ - _ - _ - \033[0m", "\033[38;5;251m _ - _ - _ \033[0m", "\033[38;5;251m _ - _ - _ - \033[0m", " ", ], } LOCALE = { "af": "af_ZA", "ar": "ar_TN", "az": "az_AZ", "be": "be_BY", "bg": "bg_BG", "bs": "bs_BA", "ca": "ca_ES", "cs": "cs_CZ", "cy": "cy_GB", "da": "da_DK", "de": "de_DE", "el": "el_GR", "eo": "eo", "es": "es_ES", "et": "et_EE", "fa": "fa_IR", "fi": "fi_FI", "fr": "fr_FR", "fy": "fy_NL", "ga": "ga_IE", "he": "he_IL", "hr": "hr_HR", "hu": "hu_HU", "hy": "hy_AM", "ia": "ia", "id": "id_ID", "is": "is_IS", "it": "it_IT", "ja": "ja_JP", "jv": "en_US", "ka": "ka_GE", "ko": "ko_KR", "kk": "kk_KZ", "ky": "ky_KG", "lt": "lt_LT", "lv": "lv_LV", "mk": "mk_MK", "ml": "ml_IN", "nb": "nb_NO", "nl": "nl_NL", "nn": "nn_NO", "pt": "pt_PT", "pt-br": "pt_BR", "pl": "pl_PL", "ro": "ro_RO", "ru": "ru_RU", "sv": "sv_SE", "sk": "sk_SK", "sl": "sl_SI", "sr": "sr_RS", "sr-lat": "sr_RS@latin", "sw": "sw_KE", "th": "th_TH", "tr": "tr_TR", "uk": "uk_UA", "uz": "uz_UZ", "vi": "vi_VN", "zh": "zh_TW", "zu": "zu_ZA", "mg": "mg_MG", } ================================================ FILE: lib/datasource/README.md ================================================ Currently wttr.in uses just one data source, but more data sources must be added. Having more data sources will increase data quality, and make it possible to select data source basing on location, or on the user's preferences. ## Possible data sources * [Open weather map](https://openweathermap.org/) * [Accu weather](https://www.accuweather.com/) * [Windy](https://www.windy.com/?26.953,75.711,5) * [Yr](https://www.yr.no/nb) * [BBC WeatherFeeds](https://support.bbc.co.uk/platform/feeds/WeatherFeeds.htm) * https://weather.gc.ca * [Bom](http://www.bom.gov.au) * [IMD](https://mausam.imd.gov.in/) * [darksky](https://darksky.net/forecast/40.7127,-74.0059/us12/en) * [weather bug](https://www.weatherbug.com/) * [weather underground](https://www.wunderground.com/) * [brightsky](https://brightsky.dev/) ## Air Quality sources * http://aqicn.org/ * https://docs.airnowapi.org/ * https://www2.purpleair.com/community/faq#hc-access-the-json ================================================ FILE: lib/duplicate_translations.py ================================================ import os def remove_colon_and_strip_from_str(line): """ Removes the colon from the line and strips the line. """ return line.replace(":", "").strip() def print_result_for_file(file_path, file_name, duplicate_entries): """ Prints the result for a given file. """ print("-" * 50) print(f"Processing file: {file_name} \n") # keep entries with more than one occurence if len(duplicate_entries) > 0: for key, value in duplicate_entries.items(): # prints debug info for each duplicate found print( f"{file_path}: \"{key}\" appears in lines {', '.join(map(str, value))}" ) else: # prints debug info, if no duplicates found 🥳 print(f"No duplicates found!") def find_duplicates(directory, debug=False): """ Reads all .txt files in a given directory and tries to detect duplicate entries. """ try: language_lookup_table = {} files = [f for f in os.listdir(directory) if f.endswith(".txt")] files.sort() if not files: print("No .txt files found in the directory.") return for file_name in files: # if file_name contains "-help" skip it for now! if "-help" in file_name: continue file_path = os.path.join(directory, file_name) try: with open(file_path, "r", encoding="utf-8") as file: lookup_table = {} for line_number, line in enumerate(file, start=1): stripped_line = line.strip() stripped_keywords = stripped_line.split(":") stripped_keywords = list( map(remove_colon_and_strip_from_str, stripped_keywords) ) trimmed_keywords = list(map(str.strip, stripped_keywords)) for tk in trimmed_keywords: if tk == "" or tk.isdigit(): continue if tk in lookup_table: lookup_table[tk].append(line_number) else: lookup_table[tk] = [line_number] duplicate_entries = { k: v for k, v in lookup_table.items() if len(v) > 1 } print_result_for_file(file_path, file_name, duplicate_entries) language_lookup_table[file_name] = duplicate_entries except Exception as e: print(f"An error occurred while processing the file: {e}") return language_lookup_table except Exception as e: print(f"An error occurred: {e}") return None # Example usage from the root directory: # python ./lib/duplicate_translations.py if __name__ == "__main__": directory_path = "share/translations/" find_duplicates(directory_path, debug=True) ================================================ FILE: lib/extract_emoji.py ================================================ #!/usr/bin/env python # vim: fileencoding=utf-8 """ At the moment, Pillow library does not support colorful emojis, that is why emojis must be extracted to external files first, and then they must be handled as usual graphical objects and not as text. The files are extracted using Imagemagick. Usage: ve/bi/python lib/extract_emoji.py """ import subprocess EMOJIS = [ "✨", "☁️", "🌫", "🌧", "🌧", "❄️", "❄️", "🌦", "🌦", "🌧", "🌧", "🌨", "🌨", "⛅️", "☀️", "🌩", "⛈", "⛈", "☁️", "🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘", ] def extract_emojis_to_directory(dirname): """ Extract emoji from an emoji font, to separate files. """ emoji_font = "Noto Color Emoji" emoji_size = 30 for emoji in EMOJIS: filename = "%s/%s.png" % (dirname, emoji) convert_string = [ "convert", "-background", "black", "-size", "%sx%s" % (emoji_size, emoji_size), "-set", "colorspace", "sRGB", 'pango:%s' % (emoji_font, emoji), filename, ] subprocess.Popen(convert_string) if __name__ == "__main__": extract_emojis_to_directory("share/emoji") ================================================ FILE: lib/fields.py ================================================ """ Human readable description of the available data fields describing current weather, weather forecast, and astronomical data """ DESCRIPTION = { # current condition fields "FeelsLikeC": ( "Feels Like Temperature in Celsius", "temperature_feels_like_celsius", ), "FeelsLikeF": ( "Feels Like Temperature in Fahrenheit", "temperature_feels_like_fahrenheit", ), "cloudcover": ("Cloud Coverage in Percent", "cloudcover_percentage"), "humidity": ("Humidity in Percent", "humidity_percentage"), "precipMM": ("Precipitation (Rainfall) in mm", "precipitation_mm"), "pressure": ("Air pressure in hPa", "pressure_hpa"), "temp_C": ("Temperature in Celsius", "temperature_celsius"), "temp_F": ("Temperature in Fahrenheit", "temperature_fahrenheit"), "uvIndex": ("Ultraviolet Radiation Index", "uv_index"), "visibility": ("Visible Distance in Kilometres", "visibility"), "weatherCode": ("Code to describe Weather Condition", "weather_code"), "winddirDegree": ("Wind Direction in Degree", "winddir_degree"), "windspeedKmph": ("Wind Speed in Kilometres per Hour", "windspeed_kmph"), "windspeedMiles": ("Wind Speed in Miles per Hour", "windspeed_mph"), "observation_time": ( "Minutes since start of the day the observation happened", "observation_time", ), # fields with `description` "weatherDesc": ("Weather Description", "weather_desc"), "winddir16Point": ("Wind Direction on a 16-wind compass rose", "winddir_16_point"), # forecast fields "maxtempC": ("Maximum Temperature in Celsius", "temperature_celsius_maximum"), "maxtempF": ("Maximum Temperature in Fahrenheit", "temperature_fahrenheit_maximum"), "mintempC": ("Minimum Temperature in Celsius", "temperature_celsius_minimum"), "mintempF": ("Minimum Temperature in Fahrenheit", "temperature_fahrenheit_minimum"), "sunHour": ("Hours of sunlight", "sun_hour"), "totalSnow_cm": ("Total snowfall in cm", "snowfall_cm"), # astronomy fields "moon_illumination": ( "Percentage of the moon illuminated", "astronomy_moon_illumination", ), # astronomy fields with description "moon_phase": ("Phase of the moon", "astronomy_moon_phase"), # astronomy fields with time "moonrise": ( "Minutes since start of the day until the moon appears above the horizon", "astronomy_moonrise_min", ), "moonset": ( "Minutes since start of the day until the moon disappears below the horizon", "astronomy_moonset_min", ), "sunrise": ( "Minutes since start of the day until the sun appears above the horizon", "astronomy_sunrise_min", ), "sunset": ( "Minutes since start of the day until the moon disappears below the horizon", "astronomy_sunset_min", ), } ================================================ FILE: lib/fmt/__init__.py ================================================ ================================================ FILE: lib/fmt/png.py ================================================ #!/usr/bin/python # vim: encoding=utf-8 # pylint: disable=wrong-import-position,wrong-import-order,redefined-builtin """ This module is used to generate png-files for wttr.in queries. The only exported function is: * render_ansi(png_file, text, options=None) `render_ansi` is the main function of the module, which does rendering of stream into a PNG-file. The module uses PIL for graphical tasks, and pyte for rendering of ANSI stream into terminal representation. """ from __future__ import print_function import sys import io import os import glob from PIL import Image, ImageFont, ImageDraw import pyte.screens import emoji import grapheme from . import unicodedata2 sys.path.insert(0, "..") import constants import globals COLS = 180 ROWS = 100 CHAR_WIDTH = 8 CHAR_HEIGHT = 14 FONT_SIZE = 13 FONT_CAT = { "default": "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", "Cyrillic": "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", "Greek": "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", "Arabic": "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", "Hebrew": "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf", "Han": "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", "Hiragana": "/usr/share/fonts/truetype/motoya-l-cedar/MTLc3m.ttf", "Katakana": "/usr/share/fonts/truetype/motoya-l-cedar/MTLc3m.ttf", "Hangul": "/usr/share/fonts/truetype/lexi/LexiGulim.ttf", "Braille": "/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf", "Emoji": "/usr/share/fonts/truetype/ancient-scripts/Symbola_hint.ttf", } # # How to find font for non-standard scripts: # # $ fc-list :lang=ja # # GNU/Debian packages, that the fonts come from: # # * fonts-dejavu-core # * fonts-wqy-zenhei (Han) # * fonts-motoya-l-cedar (Hiragana/Katakana) # * fonts-lexi-gulim (Hangul) # * fonts-symbola (Braille/Emoji) # def render_ansi(text, options=None): """Render `text` (terminal sequence) in a PNG file paying attention to passed command line `options`. Return: file content """ screen = pyte.screens.Screen(COLS, ROWS) screen.set_mode(pyte.modes.LNM) stream = pyte.Stream(screen) text, graphemes = _fix_graphemes(text) stream.feed(text) buf = sorted(screen.buffer.items(), key=lambda x: x[0]) buf = [[x[1] for x in sorted(line[1].items(), key=lambda x: x[0])] for line in buf] return _gen_term(buf, graphemes, options=options) def _color_mapping(color, inverse=False): """Convert pyte color to PIL color Return: tuple of color values (R,G,B) """ if color == "default": if inverse: return "black" return "lightgray" if color in ["green", "black", "cyan", "blue", "brown"]: return color try: return (int(color[0:2], 16), int(color[2:4], 16), int(color[4:6], 16)) except (ValueError, IndexError): # if we do not know this color and it can not be decoded as RGB, # print it and return it as it is (will be displayed as black) # print color return color return color def _strip_buf(buf): """Strips empty spaces from behind and from the right side. (from the right side is not yet implemented) """ def empty_line(line): "Returns True if the line consists from spaces" return all(x.data == " " for x in line) def line_len(line): "Returns len of the line excluding spaces from the right" last_pos = len(line) while last_pos > 0 and line[last_pos - 1].data == " ": last_pos -= 1 return last_pos number_of_lines = 0 for line in buf[::-1]: if not empty_line(line): break number_of_lines += 1 if number_of_lines: buf = buf[:-number_of_lines] max_len = max(line_len(x) for x in buf) buf = [line[:max_len] for line in buf] return buf def _script_category(char): """Returns category of a Unicode character Possible values: default, Cyrillic, Greek, Han, Hiragana """ if emoji.is_emoji(char): return "Emoji" cat = unicodedata2.script_cat(char)[0] if char == ":": return "Han" if cat in ["Latin", "Common"]: return "default" return cat def _load_emojilib(): """Load known emojis from a directory, and return dictionary of PIL Image objects correspodent to the loaded emojis. Each emoji is resized to the CHAR_HEIGHT size. """ emojilib = {} for filename in glob.glob("share/emoji/*.png"): character = os.path.basename(filename)[:-3] emojilib[character] = Image.open(filename).resize((CHAR_HEIGHT, CHAR_HEIGHT)) return emojilib # pylint: disable=too-many-locals,too-many-branches,too-many-statements def _gen_term(buf, graphemes, options=None): """Renders rendered pyte buffer `buf` and list of workaround `graphemes` to a PNG file, and return its content """ if not options: options = {} current_grapheme = 0 buf = _strip_buf(buf) cols = max(len(x) for x in buf) rows = len(buf) bg_color = 0 if "background" in options: bg_color = _color_mapping(options["background"], options.get("inverted_colors")) image = Image.new("RGB", (cols * CHAR_WIDTH, rows * CHAR_HEIGHT), color=bg_color) buf = buf[-ROWS:] draw = ImageDraw.Draw(image) font = {} for cat in FONT_CAT: font[cat] = ImageFont.truetype(FONT_CAT[cat], FONT_SIZE) emojilib = _load_emojilib() x_pos = 0 y_pos = 0 for line in buf: x_pos = 0 for char in line: current_color = _color_mapping(char.fg, options.get("inverted_colors")) if char.bg != "default": draw.rectangle( ((x_pos, y_pos), (x_pos + CHAR_WIDTH, y_pos + CHAR_HEIGHT)), fill=_color_mapping(char.bg, options.get("inverted_colors")), ) if char.data == "!": try: data = graphemes[current_grapheme] except IndexError: pass current_grapheme += 1 else: data = char.data if data: cat = _script_category(data[0]) if cat not in font: globals.log("Unknown font category: %s" % cat) if cat == "Emoji" and emojilib.get(data): image.paste(emojilib.get(data), (x_pos, y_pos)) else: draw.text( (x_pos, y_pos), data, font=font.get(cat, font.get("default")), fill=current_color, ) x_pos += CHAR_WIDTH * constants.WEATHER_SYMBOL_WIDTH_VTE.get(data, 1) y_pos += CHAR_HEIGHT if "transparency" in options: transparency = options.get("transparency", "255") try: transparency = int(transparency) except ValueError: transparency = 255 if transparency < 0: transparency = 0 if transparency > 255: transparency = 255 image = image.convert("RGBA") datas = image.getdata() new_data = [] for item in datas: new_item = tuple(list(item[:3]) + [transparency]) new_data.append(new_item) image.putdata(new_data) img_bytes = io.BytesIO() image.save(img_bytes, format="png") return img_bytes.getvalue() def _fix_graphemes(text): """ Extract long graphemes sequences that can't be handled by pyte correctly because of the bug pyte#131. Graphemes are omited and replaced with placeholders, and returned as a list. Return: text_without_graphemes, graphemes """ output = "" graphemes = [] for gra in grapheme.graphemes(text): if len(gra) > 1: character = "!" graphemes.append(gra) else: character = gra output += character return output, graphemes ================================================ FILE: lib/fmt/unicodedata2.py ================================================ # downloaded from https://gist.github.com/2204527 # described/recommended here: # # http://stackoverflow.com/questions/9868792/find-out-the-unicode-script-of-a-character # from __future__ import print_function from unicodedata import * script_data = { "names": [ "Common", "Latin", "Greek", "Cyrillic", "Armenian", "Hebrew", "Arabic", "Syriac", "Thaana", "Devanagari", "Bengali", "Gurmukhi", "Gujarati", "Oriya", "Tamil", "Telugu", "Kannada", "Malayalam", "Sinhala", "Thai", "Lao", "Tibetan", "Myanmar", "Georgian", "Hangul", "Ethiopic", "Cherokee", "Canadian_Aboriginal", "Ogham", "Runic", "Khmer", "Mongolian", "Hiragana", "Katakana", "Bopomofo", "Han", "Yi", "Old_Italic", "Gothic", "Deseret", "Inherited", "Tagalog", "Hanunoo", "Buhid", "Tagbanwa", "Limbu", "Tai_Le", "Linear_B", "Ugaritic", "Shavian", "Osmanya", "Cypriot", "Braille", "Buginese", "Coptic", "New_Tai_Lue", "Glagolitic", "Tifinagh", "Syloti_Nagri", "Old_Persian", "Kharoshthi", "Balinese", "Cuneiform", "Phoenician", "Phags_Pa", "Nko", "Sundanese", "Lepcha", "Ol_Chiki", "Vai", "Saurashtra", "Kayah_Li", "Rejang", "Lycian", "Carian", "Lydian", "Cham", "Tai_Tham", "Tai_Viet", "Avestan", "Egyptian_Hieroglyphs", "Samaritan", "Lisu", "Bamum", "Javanese", "Meetei_Mayek", "Imperial_Aramaic", "Old_South_Arabian", "Inscriptional_Parthian", "Inscriptional_Pahlavi", "Old_Turkic", "Kaithi", "Batak", "Brahmi", "Mandaic", "Chakma", "Meroitic_Cursive", "Meroitic_Hieroglyphs", "Miao", "Sharada", "Sora_Sompeng", "Takri", ], "cats": [ "Cc", "Zs", "Po", "Sc", "Ps", "Pe", "Sm", "Pd", "Nd", "Sk", "Pc", "So", "Pi", "Cf", "No", "L", "Pf", "Lm", "Mc", "Lo", "Zl", "Zp", "Nl", "Mn", "Me", ], "idx": [ (0x0, 0x1F, 0, 0), (0x20, 0x20, 0, 1), (0x21, 0x23, 0, 2), (0x24, 0x24, 0, 3), (0x25, 0x27, 0, 2), (0x28, 0x28, 0, 4), (0x29, 0x29, 0, 5), (0x2A, 0x2A, 0, 2), (0x2B, 0x2B, 0, 6), (0x2C, 0x2C, 0, 2), (0x2D, 0x2D, 0, 7), (0x2E, 0x2F, 0, 2), (0x30, 0x39, 0, 8), (0x3A, 0x3B, 0, 2), (0x3C, 0x3E, 0, 6), (0x3F, 0x40, 0, 2), (0x41, 0x5A, 1, 15), (0x5B, 0x5B, 0, 4), (0x5C, 0x5C, 0, 2), (0x5D, 0x5D, 0, 5), (0x5E, 0x5E, 0, 9), (0x5F, 0x5F, 0, 10), (0x60, 0x60, 0, 9), (0x61, 0x7A, 1, 15), (0x7B, 0x7B, 0, 4), (0x7C, 0x7C, 0, 6), (0x7D, 0x7D, 0, 5), (0x7E, 0x7E, 0, 6), (0x7F, 0x9F, 0, 0), (0xA0, 0xA0, 0, 1), (0xA1, 0xA1, 0, 2), (0xA2, 0xA5, 0, 3), (0xA6, 0xA6, 0, 11), (0xA7, 0xA7, 0, 2), (0xA8, 0xA8, 0, 9), (0xA9, 0xA9, 0, 11), (0xAA, 0xAA, 1, 19), (0xAB, 0xAB, 0, 12), (0xAC, 0xAC, 0, 6), (0xAD, 0xAD, 0, 13), (0xAE, 0xAE, 0, 11), (0xAF, 0xAF, 0, 9), (0xB0, 0xB0, 0, 11), (0xB1, 0xB1, 0, 6), (0xB2, 0xB3, 0, 14), (0xB4, 0xB4, 0, 9), (0xB5, 0xB5, 0, 15), (0xB6, 0xB7, 0, 2), (0xB8, 0xB8, 0, 9), (0xB9, 0xB9, 0, 14), (0xBA, 0xBA, 1, 19), (0xBB, 0xBB, 0, 16), (0xBC, 0xBE, 0, 14), (0xBF, 0xBF, 0, 2), (0xC0, 0xD6, 1, 15), (0xD7, 0xD7, 0, 6), (0xD8, 0xF6, 1, 15), (0xF7, 0xF7, 0, 6), (0xF8, 0x1BA, 1, 15), (0x1BB, 0x1BB, 1, 19), (0x1BC, 0x1BF, 1, 15), (0x1C0, 0x1C3, 1, 19), (0x1C4, 0x293, 1, 15), (0x294, 0x294, 1, 19), (0x295, 0x2AF, 1, 15), (0x2B0, 0x2B8, 1, 17), (0x2B9, 0x2C1, 0, 17), (0x2C2, 0x2C5, 0, 9), (0x2C6, 0x2D1, 0, 17), (0x2D2, 0x2DF, 0, 9), (0x2E0, 0x2E4, 1, 17), (0x2E5, 0x2E9, 0, 9), (0x2EA, 0x2EB, 34, 9), (0x2EC, 0x2EC, 0, 17), (0x2ED, 0x2ED, 0, 9), (0x2EE, 0x2EE, 0, 17), (0x2EF, 0x2FF, 0, 9), (0x300, 0x36F, 40, 23), (0x370, 0x373, 2, 15), (0x374, 0x374, 0, 17), (0x375, 0x375, 2, 9), (0x376, 0x377, 2, 15), (0x37A, 0x37A, 2, 17), (0x37B, 0x37D, 2, 15), (0x37E, 0x37E, 0, 2), (0x384, 0x384, 2, 9), (0x385, 0x385, 0, 9), (0x386, 0x386, 2, 15), (0x387, 0x387, 0, 2), (0x388, 0x38A, 2, 15), (0x38C, 0x38C, 2, 15), (0x38E, 0x3A1, 2, 15), (0x3A3, 0x3E1, 2, 15), (0x3E2, 0x3EF, 54, 15), (0x3F0, 0x3F5, 2, 15), (0x3F6, 0x3F6, 2, 6), (0x3F7, 0x3FF, 2, 15), (0x400, 0x481, 3, 15), (0x482, 0x482, 3, 11), (0x483, 0x484, 3, 23), (0x485, 0x486, 40, 23), (0x487, 0x487, 3, 23), (0x488, 0x489, 3, 24), (0x48A, 0x527, 3, 15), (0x531, 0x556, 4, 15), (0x559, 0x559, 4, 17), (0x55A, 0x55F, 4, 2), (0x561, 0x587, 4, 15), (0x589, 0x589, 0, 2), (0x58A, 0x58A, 4, 7), (0x58F, 0x58F, 4, 3), (0x591, 0x5BD, 5, 23), (0x5BE, 0x5BE, 5, 7), (0x5BF, 0x5BF, 5, 23), (0x5C0, 0x5C0, 5, 2), (0x5C1, 0x5C2, 5, 23), (0x5C3, 0x5C3, 5, 2), (0x5C4, 0x5C5, 5, 23), (0x5C6, 0x5C6, 5, 2), (0x5C7, 0x5C7, 5, 23), (0x5D0, 0x5EA, 5, 19), (0x5F0, 0x5F2, 5, 19), (0x5F3, 0x5F4, 5, 2), (0x600, 0x604, 6, 13), (0x606, 0x608, 6, 6), (0x609, 0x60A, 6, 2), (0x60B, 0x60B, 6, 3), (0x60C, 0x60C, 0, 2), (0x60D, 0x60D, 6, 2), (0x60E, 0x60F, 6, 11), (0x610, 0x61A, 6, 23), (0x61B, 0x61B, 0, 2), (0x61E, 0x61E, 6, 2), (0x61F, 0x61F, 0, 2), (0x620, 0x63F, 6, 19), (0x640, 0x640, 0, 17), (0x641, 0x64A, 6, 19), (0x64B, 0x655, 40, 23), (0x656, 0x65E, 6, 23), (0x65F, 0x65F, 40, 23), (0x660, 0x669, 0, 8), (0x66A, 0x66D, 6, 2), (0x66E, 0x66F, 6, 19), (0x670, 0x670, 40, 23), (0x671, 0x6D3, 6, 19), (0x6D4, 0x6D4, 6, 2), (0x6D5, 0x6D5, 6, 19), (0x6D6, 0x6DC, 6, 23), (0x6DD, 0x6DD, 0, 13), (0x6DE, 0x6DE, 6, 11), (0x6DF, 0x6E4, 6, 23), (0x6E5, 0x6E6, 6, 17), (0x6E7, 0x6E8, 6, 23), (0x6E9, 0x6E9, 6, 11), (0x6EA, 0x6ED, 6, 23), (0x6EE, 0x6EF, 6, 19), (0x6F0, 0x6F9, 6, 8), (0x6FA, 0x6FC, 6, 19), (0x6FD, 0x6FE, 6, 11), (0x6FF, 0x6FF, 6, 19), (0x700, 0x70D, 7, 2), (0x70F, 0x70F, 7, 13), (0x710, 0x710, 7, 19), (0x711, 0x711, 7, 23), (0x712, 0x72F, 7, 19), (0x730, 0x74A, 7, 23), (0x74D, 0x74F, 7, 19), (0x750, 0x77F, 6, 19), (0x780, 0x7A5, 8, 19), (0x7A6, 0x7B0, 8, 23), (0x7B1, 0x7B1, 8, 19), (0x7C0, 0x7C9, 65, 8), (0x7CA, 0x7EA, 65, 19), (0x7EB, 0x7F3, 65, 23), (0x7F4, 0x7F5, 65, 17), (0x7F6, 0x7F6, 65, 11), (0x7F7, 0x7F9, 65, 2), (0x7FA, 0x7FA, 65, 17), (0x800, 0x815, 81, 19), (0x816, 0x819, 81, 23), (0x81A, 0x81A, 81, 17), (0x81B, 0x823, 81, 23), (0x824, 0x824, 81, 17), (0x825, 0x827, 81, 23), (0x828, 0x828, 81, 17), (0x829, 0x82D, 81, 23), (0x830, 0x83E, 81, 2), (0x840, 0x858, 94, 19), (0x859, 0x85B, 94, 23), (0x85E, 0x85E, 94, 2), (0x8A0, 0x8A0, 6, 19), (0x8A2, 0x8AC, 6, 19), (0x8E4, 0x8FE, 6, 23), (0x900, 0x902, 9, 23), (0x903, 0x903, 9, 18), (0x904, 0x939, 9, 19), (0x93A, 0x93A, 9, 23), (0x93B, 0x93B, 9, 18), (0x93C, 0x93C, 9, 23), (0x93D, 0x93D, 9, 19), (0x93E, 0x940, 9, 18), (0x941, 0x948, 9, 23), (0x949, 0x94C, 9, 18), (0x94D, 0x94D, 9, 23), (0x94E, 0x94F, 9, 18), (0x950, 0x950, 9, 19), (0x951, 0x952, 40, 23), (0x953, 0x957, 9, 23), (0x958, 0x961, 9, 19), (0x962, 0x963, 9, 23), (0x964, 0x965, 0, 2), (0x966, 0x96F, 9, 8), (0x970, 0x970, 9, 2), (0x971, 0x971, 9, 17), (0x972, 0x977, 9, 19), (0x979, 0x97F, 9, 19), (0x981, 0x981, 10, 23), (0x982, 0x983, 10, 18), (0x985, 0x98C, 10, 19), (0x98F, 0x990, 10, 19), (0x993, 0x9A8, 10, 19), (0x9AA, 0x9B0, 10, 19), (0x9B2, 0x9B2, 10, 19), (0x9B6, 0x9B9, 10, 19), (0x9BC, 0x9BC, 10, 23), (0x9BD, 0x9BD, 10, 19), (0x9BE, 0x9C0, 10, 18), (0x9C1, 0x9C4, 10, 23), (0x9C7, 0x9C8, 10, 18), (0x9CB, 0x9CC, 10, 18), (0x9CD, 0x9CD, 10, 23), (0x9CE, 0x9CE, 10, 19), (0x9D7, 0x9D7, 10, 18), (0x9DC, 0x9DD, 10, 19), (0x9DF, 0x9E1, 10, 19), (0x9E2, 0x9E3, 10, 23), (0x9E6, 0x9EF, 10, 8), (0x9F0, 0x9F1, 10, 19), (0x9F2, 0x9F3, 10, 3), (0x9F4, 0x9F9, 10, 14), (0x9FA, 0x9FA, 10, 11), (0x9FB, 0x9FB, 10, 3), (0xA01, 0xA02, 11, 23), (0xA03, 0xA03, 11, 18), (0xA05, 0xA0A, 11, 19), (0xA0F, 0xA10, 11, 19), (0xA13, 0xA28, 11, 19), (0xA2A, 0xA30, 11, 19), (0xA32, 0xA33, 11, 19), (0xA35, 0xA36, 11, 19), (0xA38, 0xA39, 11, 19), (0xA3C, 0xA3C, 11, 23), (0xA3E, 0xA40, 11, 18), (0xA41, 0xA42, 11, 23), (0xA47, 0xA48, 11, 23), (0xA4B, 0xA4D, 11, 23), (0xA51, 0xA51, 11, 23), (0xA59, 0xA5C, 11, 19), (0xA5E, 0xA5E, 11, 19), (0xA66, 0xA6F, 11, 8), (0xA70, 0xA71, 11, 23), (0xA72, 0xA74, 11, 19), (0xA75, 0xA75, 11, 23), (0xA81, 0xA82, 12, 23), (0xA83, 0xA83, 12, 18), (0xA85, 0xA8D, 12, 19), (0xA8F, 0xA91, 12, 19), (0xA93, 0xAA8, 12, 19), (0xAAA, 0xAB0, 12, 19), (0xAB2, 0xAB3, 12, 19), (0xAB5, 0xAB9, 12, 19), (0xABC, 0xABC, 12, 23), (0xABD, 0xABD, 12, 19), (0xABE, 0xAC0, 12, 18), (0xAC1, 0xAC5, 12, 23), (0xAC7, 0xAC8, 12, 23), (0xAC9, 0xAC9, 12, 18), (0xACB, 0xACC, 12, 18), (0xACD, 0xACD, 12, 23), (0xAD0, 0xAD0, 12, 19), (0xAE0, 0xAE1, 12, 19), (0xAE2, 0xAE3, 12, 23), (0xAE6, 0xAEF, 12, 8), (0xAF0, 0xAF0, 12, 2), (0xAF1, 0xAF1, 12, 3), (0xB01, 0xB01, 13, 23), (0xB02, 0xB03, 13, 18), (0xB05, 0xB0C, 13, 19), (0xB0F, 0xB10, 13, 19), (0xB13, 0xB28, 13, 19), (0xB2A, 0xB30, 13, 19), (0xB32, 0xB33, 13, 19), (0xB35, 0xB39, 13, 19), (0xB3C, 0xB3C, 13, 23), (0xB3D, 0xB3D, 13, 19), (0xB3E, 0xB3E, 13, 18), (0xB3F, 0xB3F, 13, 23), (0xB40, 0xB40, 13, 18), (0xB41, 0xB44, 13, 23), (0xB47, 0xB48, 13, 18), (0xB4B, 0xB4C, 13, 18), (0xB4D, 0xB4D, 13, 23), (0xB56, 0xB56, 13, 23), (0xB57, 0xB57, 13, 18), (0xB5C, 0xB5D, 13, 19), (0xB5F, 0xB61, 13, 19), (0xB62, 0xB63, 13, 23), (0xB66, 0xB6F, 13, 8), (0xB70, 0xB70, 13, 11), (0xB71, 0xB71, 13, 19), (0xB72, 0xB77, 13, 14), (0xB82, 0xB82, 14, 23), (0xB83, 0xB83, 14, 19), (0xB85, 0xB8A, 14, 19), (0xB8E, 0xB90, 14, 19), (0xB92, 0xB95, 14, 19), (0xB99, 0xB9A, 14, 19), (0xB9C, 0xB9C, 14, 19), (0xB9E, 0xB9F, 14, 19), (0xBA3, 0xBA4, 14, 19), (0xBA8, 0xBAA, 14, 19), (0xBAE, 0xBB9, 14, 19), (0xBBE, 0xBBF, 14, 18), (0xBC0, 0xBC0, 14, 23), (0xBC1, 0xBC2, 14, 18), (0xBC6, 0xBC8, 14, 18), (0xBCA, 0xBCC, 14, 18), (0xBCD, 0xBCD, 14, 23), (0xBD0, 0xBD0, 14, 19), (0xBD7, 0xBD7, 14, 18), (0xBE6, 0xBEF, 14, 8), (0xBF0, 0xBF2, 14, 14), (0xBF3, 0xBF8, 14, 11), (0xBF9, 0xBF9, 14, 3), (0xBFA, 0xBFA, 14, 11), (0xC01, 0xC03, 15, 18), (0xC05, 0xC0C, 15, 19), (0xC0E, 0xC10, 15, 19), (0xC12, 0xC28, 15, 19), (0xC2A, 0xC33, 15, 19), (0xC35, 0xC39, 15, 19), (0xC3D, 0xC3D, 15, 19), (0xC3E, 0xC40, 15, 23), (0xC41, 0xC44, 15, 18), (0xC46, 0xC48, 15, 23), (0xC4A, 0xC4D, 15, 23), (0xC55, 0xC56, 15, 23), (0xC58, 0xC59, 15, 19), (0xC60, 0xC61, 15, 19), (0xC62, 0xC63, 15, 23), (0xC66, 0xC6F, 15, 8), (0xC78, 0xC7E, 15, 14), (0xC7F, 0xC7F, 15, 11), (0xC82, 0xC83, 16, 18), (0xC85, 0xC8C, 16, 19), (0xC8E, 0xC90, 16, 19), (0xC92, 0xCA8, 16, 19), (0xCAA, 0xCB3, 16, 19), (0xCB5, 0xCB9, 16, 19), (0xCBC, 0xCBC, 16, 23), (0xCBD, 0xCBD, 16, 19), (0xCBE, 0xCBE, 16, 18), (0xCBF, 0xCBF, 16, 23), (0xCC0, 0xCC4, 16, 18), (0xCC6, 0xCC6, 16, 23), (0xCC7, 0xCC8, 16, 18), (0xCCA, 0xCCB, 16, 18), (0xCCC, 0xCCD, 16, 23), (0xCD5, 0xCD6, 16, 18), (0xCDE, 0xCDE, 16, 19), (0xCE0, 0xCE1, 16, 19), (0xCE2, 0xCE3, 16, 23), (0xCE6, 0xCEF, 16, 8), (0xCF1, 0xCF2, 16, 19), (0xD02, 0xD03, 17, 18), (0xD05, 0xD0C, 17, 19), (0xD0E, 0xD10, 17, 19), (0xD12, 0xD3A, 17, 19), (0xD3D, 0xD3D, 17, 19), (0xD3E, 0xD40, 17, 18), (0xD41, 0xD44, 17, 23), (0xD46, 0xD48, 17, 18), (0xD4A, 0xD4C, 17, 18), (0xD4D, 0xD4D, 17, 23), (0xD4E, 0xD4E, 17, 19), (0xD57, 0xD57, 17, 18), (0xD60, 0xD61, 17, 19), (0xD62, 0xD63, 17, 23), (0xD66, 0xD6F, 17, 8), (0xD70, 0xD75, 17, 14), (0xD79, 0xD79, 17, 11), (0xD7A, 0xD7F, 17, 19), (0xD82, 0xD83, 18, 18), (0xD85, 0xD96, 18, 19), (0xD9A, 0xDB1, 18, 19), (0xDB3, 0xDBB, 18, 19), (0xDBD, 0xDBD, 18, 19), (0xDC0, 0xDC6, 18, 19), (0xDCA, 0xDCA, 18, 23), (0xDCF, 0xDD1, 18, 18), (0xDD2, 0xDD4, 18, 23), (0xDD6, 0xDD6, 18, 23), (0xDD8, 0xDDF, 18, 18), (0xDF2, 0xDF3, 18, 18), (0xDF4, 0xDF4, 18, 2), (0xE01, 0xE30, 19, 19), (0xE31, 0xE31, 19, 23), (0xE32, 0xE33, 19, 19), (0xE34, 0xE3A, 19, 23), (0xE3F, 0xE3F, 0, 3), (0xE40, 0xE45, 19, 19), (0xE46, 0xE46, 19, 17), (0xE47, 0xE4E, 19, 23), (0xE4F, 0xE4F, 19, 2), (0xE50, 0xE59, 19, 8), (0xE5A, 0xE5B, 19, 2), (0xE81, 0xE82, 20, 19), (0xE84, 0xE84, 20, 19), (0xE87, 0xE88, 20, 19), (0xE8A, 0xE8A, 20, 19), (0xE8D, 0xE8D, 20, 19), (0xE94, 0xE97, 20, 19), (0xE99, 0xE9F, 20, 19), (0xEA1, 0xEA3, 20, 19), (0xEA5, 0xEA5, 20, 19), (0xEA7, 0xEA7, 20, 19), (0xEAA, 0xEAB, 20, 19), (0xEAD, 0xEB0, 20, 19), (0xEB1, 0xEB1, 20, 23), (0xEB2, 0xEB3, 20, 19), (0xEB4, 0xEB9, 20, 23), (0xEBB, 0xEBC, 20, 23), (0xEBD, 0xEBD, 20, 19), (0xEC0, 0xEC4, 20, 19), (0xEC6, 0xEC6, 20, 17), (0xEC8, 0xECD, 20, 23), (0xED0, 0xED9, 20, 8), (0xEDC, 0xEDF, 20, 19), (0xF00, 0xF00, 21, 19), (0xF01, 0xF03, 21, 11), (0xF04, 0xF12, 21, 2), (0xF13, 0xF13, 21, 11), (0xF14, 0xF14, 21, 2), (0xF15, 0xF17, 21, 11), (0xF18, 0xF19, 21, 23), (0xF1A, 0xF1F, 21, 11), (0xF20, 0xF29, 21, 8), (0xF2A, 0xF33, 21, 14), (0xF34, 0xF34, 21, 11), (0xF35, 0xF35, 21, 23), (0xF36, 0xF36, 21, 11), (0xF37, 0xF37, 21, 23), (0xF38, 0xF38, 21, 11), (0xF39, 0xF39, 21, 23), (0xF3A, 0xF3A, 21, 4), (0xF3B, 0xF3B, 21, 5), (0xF3C, 0xF3C, 21, 4), (0xF3D, 0xF3D, 21, 5), (0xF3E, 0xF3F, 21, 18), (0xF40, 0xF47, 21, 19), (0xF49, 0xF6C, 21, 19), (0xF71, 0xF7E, 21, 23), (0xF7F, 0xF7F, 21, 18), (0xF80, 0xF84, 21, 23), (0xF85, 0xF85, 21, 2), (0xF86, 0xF87, 21, 23), (0xF88, 0xF8C, 21, 19), (0xF8D, 0xF97, 21, 23), (0xF99, 0xFBC, 21, 23), (0xFBE, 0xFC5, 21, 11), (0xFC6, 0xFC6, 21, 23), (0xFC7, 0xFCC, 21, 11), (0xFCE, 0xFCF, 21, 11), (0xFD0, 0xFD4, 21, 2), (0xFD5, 0xFD8, 0, 11), (0xFD9, 0xFDA, 21, 2), (0x1000, 0x102A, 22, 19), (0x102B, 0x102C, 22, 18), (0x102D, 0x1030, 22, 23), (0x1031, 0x1031, 22, 18), (0x1032, 0x1037, 22, 23), (0x1038, 0x1038, 22, 18), (0x1039, 0x103A, 22, 23), (0x103B, 0x103C, 22, 18), (0x103D, 0x103E, 22, 23), (0x103F, 0x103F, 22, 19), (0x1040, 0x1049, 22, 8), (0x104A, 0x104F, 22, 2), (0x1050, 0x1055, 22, 19), (0x1056, 0x1057, 22, 18), (0x1058, 0x1059, 22, 23), (0x105A, 0x105D, 22, 19), (0x105E, 0x1060, 22, 23), (0x1061, 0x1061, 22, 19), (0x1062, 0x1064, 22, 18), (0x1065, 0x1066, 22, 19), (0x1067, 0x106D, 22, 18), (0x106E, 0x1070, 22, 19), (0x1071, 0x1074, 22, 23), (0x1075, 0x1081, 22, 19), (0x1082, 0x1082, 22, 23), (0x1083, 0x1084, 22, 18), (0x1085, 0x1086, 22, 23), (0x1087, 0x108C, 22, 18), (0x108D, 0x108D, 22, 23), (0x108E, 0x108E, 22, 19), (0x108F, 0x108F, 22, 18), (0x1090, 0x1099, 22, 8), (0x109A, 0x109C, 22, 18), (0x109D, 0x109D, 22, 23), (0x109E, 0x109F, 22, 11), (0x10A0, 0x10C5, 23, 15), (0x10C7, 0x10C7, 23, 15), (0x10CD, 0x10CD, 23, 15), (0x10D0, 0x10FA, 23, 19), (0x10FB, 0x10FB, 0, 2), (0x10FC, 0x10FC, 23, 17), (0x10FD, 0x10FF, 23, 19), (0x1100, 0x11FF, 24, 19), (0x1200, 0x1248, 25, 19), (0x124A, 0x124D, 25, 19), (0x1250, 0x1256, 25, 19), (0x1258, 0x1258, 25, 19), (0x125A, 0x125D, 25, 19), (0x1260, 0x1288, 25, 19), (0x128A, 0x128D, 25, 19), (0x1290, 0x12B0, 25, 19), (0x12B2, 0x12B5, 25, 19), (0x12B8, 0x12BE, 25, 19), (0x12C0, 0x12C0, 25, 19), (0x12C2, 0x12C5, 25, 19), (0x12C8, 0x12D6, 25, 19), (0x12D8, 0x1310, 25, 19), (0x1312, 0x1315, 25, 19), (0x1318, 0x135A, 25, 19), (0x135D, 0x135F, 25, 23), (0x1360, 0x1368, 25, 2), (0x1369, 0x137C, 25, 14), (0x1380, 0x138F, 25, 19), (0x1390, 0x1399, 25, 11), (0x13A0, 0x13F4, 26, 19), (0x1400, 0x1400, 27, 7), (0x1401, 0x166C, 27, 19), (0x166D, 0x166E, 27, 2), (0x166F, 0x167F, 27, 19), (0x1680, 0x1680, 28, 1), (0x1681, 0x169A, 28, 19), (0x169B, 0x169B, 28, 4), (0x169C, 0x169C, 28, 5), (0x16A0, 0x16EA, 29, 19), (0x16EB, 0x16ED, 0, 2), (0x16EE, 0x16F0, 29, 22), (0x1700, 0x170C, 41, 19), (0x170E, 0x1711, 41, 19), (0x1712, 0x1714, 41, 23), (0x1720, 0x1731, 42, 19), (0x1732, 0x1734, 42, 23), (0x1735, 0x1736, 0, 2), (0x1740, 0x1751, 43, 19), (0x1752, 0x1753, 43, 23), (0x1760, 0x176C, 44, 19), (0x176E, 0x1770, 44, 19), (0x1772, 0x1773, 44, 23), (0x1780, 0x17B3, 30, 19), (0x17B4, 0x17B5, 30, 23), (0x17B6, 0x17B6, 30, 18), (0x17B7, 0x17BD, 30, 23), (0x17BE, 0x17C5, 30, 18), (0x17C6, 0x17C6, 30, 23), (0x17C7, 0x17C8, 30, 18), (0x17C9, 0x17D3, 30, 23), (0x17D4, 0x17D6, 30, 2), (0x17D7, 0x17D7, 30, 17), (0x17D8, 0x17DA, 30, 2), (0x17DB, 0x17DB, 30, 3), (0x17DC, 0x17DC, 30, 19), (0x17DD, 0x17DD, 30, 23), (0x17E0, 0x17E9, 30, 8), (0x17F0, 0x17F9, 30, 14), (0x1800, 0x1801, 31, 2), (0x1802, 0x1803, 0, 2), (0x1804, 0x1804, 31, 2), (0x1805, 0x1805, 0, 2), (0x1806, 0x1806, 31, 7), (0x1807, 0x180A, 31, 2), (0x180B, 0x180D, 31, 23), (0x180E, 0x180E, 31, 1), (0x1810, 0x1819, 31, 8), (0x1820, 0x1842, 31, 19), (0x1843, 0x1843, 31, 17), (0x1844, 0x1877, 31, 19), (0x1880, 0x18A8, 31, 19), (0x18A9, 0x18A9, 31, 23), (0x18AA, 0x18AA, 31, 19), (0x18B0, 0x18F5, 27, 19), (0x1900, 0x191C, 45, 19), (0x1920, 0x1922, 45, 23), (0x1923, 0x1926, 45, 18), (0x1927, 0x1928, 45, 23), (0x1929, 0x192B, 45, 18), (0x1930, 0x1931, 45, 18), (0x1932, 0x1932, 45, 23), (0x1933, 0x1938, 45, 18), (0x1939, 0x193B, 45, 23), (0x1940, 0x1940, 45, 11), (0x1944, 0x1945, 45, 2), (0x1946, 0x194F, 45, 8), (0x1950, 0x196D, 46, 19), (0x1970, 0x1974, 46, 19), (0x1980, 0x19AB, 55, 19), (0x19B0, 0x19C0, 55, 18), (0x19C1, 0x19C7, 55, 19), (0x19C8, 0x19C9, 55, 18), (0x19D0, 0x19D9, 55, 8), (0x19DA, 0x19DA, 55, 14), (0x19DE, 0x19DF, 55, 11), (0x19E0, 0x19FF, 30, 11), (0x1A00, 0x1A16, 53, 19), (0x1A17, 0x1A18, 53, 23), (0x1A19, 0x1A1B, 53, 18), (0x1A1E, 0x1A1F, 53, 2), (0x1A20, 0x1A54, 77, 19), (0x1A55, 0x1A55, 77, 18), (0x1A56, 0x1A56, 77, 23), (0x1A57, 0x1A57, 77, 18), (0x1A58, 0x1A5E, 77, 23), (0x1A60, 0x1A60, 77, 23), (0x1A61, 0x1A61, 77, 18), (0x1A62, 0x1A62, 77, 23), (0x1A63, 0x1A64, 77, 18), (0x1A65, 0x1A6C, 77, 23), (0x1A6D, 0x1A72, 77, 18), (0x1A73, 0x1A7C, 77, 23), (0x1A7F, 0x1A7F, 77, 23), (0x1A80, 0x1A89, 77, 8), (0x1A90, 0x1A99, 77, 8), (0x1AA0, 0x1AA6, 77, 2), (0x1AA7, 0x1AA7, 77, 17), (0x1AA8, 0x1AAD, 77, 2), (0x1B00, 0x1B03, 61, 23), (0x1B04, 0x1B04, 61, 18), (0x1B05, 0x1B33, 61, 19), (0x1B34, 0x1B34, 61, 23), (0x1B35, 0x1B35, 61, 18), (0x1B36, 0x1B3A, 61, 23), (0x1B3B, 0x1B3B, 61, 18), (0x1B3C, 0x1B3C, 61, 23), (0x1B3D, 0x1B41, 61, 18), (0x1B42, 0x1B42, 61, 23), (0x1B43, 0x1B44, 61, 18), (0x1B45, 0x1B4B, 61, 19), (0x1B50, 0x1B59, 61, 8), (0x1B5A, 0x1B60, 61, 2), (0x1B61, 0x1B6A, 61, 11), (0x1B6B, 0x1B73, 61, 23), (0x1B74, 0x1B7C, 61, 11), (0x1B80, 0x1B81, 66, 23), (0x1B82, 0x1B82, 66, 18), (0x1B83, 0x1BA0, 66, 19), (0x1BA1, 0x1BA1, 66, 18), (0x1BA2, 0x1BA5, 66, 23), (0x1BA6, 0x1BA7, 66, 18), (0x1BA8, 0x1BA9, 66, 23), (0x1BAA, 0x1BAA, 66, 18), (0x1BAB, 0x1BAB, 66, 23), (0x1BAC, 0x1BAD, 66, 18), (0x1BAE, 0x1BAF, 66, 19), (0x1BB0, 0x1BB9, 66, 8), (0x1BBA, 0x1BBF, 66, 19), (0x1BC0, 0x1BE5, 92, 19), (0x1BE6, 0x1BE6, 92, 23), (0x1BE7, 0x1BE7, 92, 18), (0x1BE8, 0x1BE9, 92, 23), (0x1BEA, 0x1BEC, 92, 18), (0x1BED, 0x1BED, 92, 23), (0x1BEE, 0x1BEE, 92, 18), (0x1BEF, 0x1BF1, 92, 23), (0x1BF2, 0x1BF3, 92, 18), (0x1BFC, 0x1BFF, 92, 2), (0x1C00, 0x1C23, 67, 19), (0x1C24, 0x1C2B, 67, 18), (0x1C2C, 0x1C33, 67, 23), (0x1C34, 0x1C35, 67, 18), (0x1C36, 0x1C37, 67, 23), (0x1C3B, 0x1C3F, 67, 2), (0x1C40, 0x1C49, 67, 8), (0x1C4D, 0x1C4F, 67, 19), (0x1C50, 0x1C59, 68, 8), (0x1C5A, 0x1C77, 68, 19), (0x1C78, 0x1C7D, 68, 17), (0x1C7E, 0x1C7F, 68, 2), (0x1CC0, 0x1CC7, 66, 2), (0x1CD0, 0x1CD2, 40, 23), (0x1CD3, 0x1CD3, 0, 2), (0x1CD4, 0x1CE0, 40, 23), (0x1CE1, 0x1CE1, 0, 18), (0x1CE2, 0x1CE8, 40, 23), (0x1CE9, 0x1CEC, 0, 19), (0x1CED, 0x1CED, 40, 23), (0x1CEE, 0x1CF1, 0, 19), (0x1CF2, 0x1CF3, 0, 18), (0x1CF4, 0x1CF4, 40, 23), (0x1CF5, 0x1CF6, 0, 19), (0x1D00, 0x1D25, 1, 15), (0x1D26, 0x1D2A, 2, 15), (0x1D2B, 0x1D2B, 3, 15), (0x1D2C, 0x1D5C, 1, 17), (0x1D5D, 0x1D61, 2, 17), (0x1D62, 0x1D65, 1, 17), (0x1D66, 0x1D6A, 2, 17), (0x1D6B, 0x1D77, 1, 15), (0x1D78, 0x1D78, 3, 17), (0x1D79, 0x1D9A, 1, 15), (0x1D9B, 0x1DBE, 1, 17), (0x1DBF, 0x1DBF, 2, 17), (0x1DC0, 0x1DE6, 40, 23), (0x1DFC, 0x1DFF, 40, 23), (0x1E00, 0x1EFF, 1, 15), (0x1F00, 0x1F15, 2, 15), (0x1F18, 0x1F1D, 2, 15), (0x1F20, 0x1F45, 2, 15), (0x1F48, 0x1F4D, 2, 15), (0x1F50, 0x1F57, 2, 15), (0x1F59, 0x1F59, 2, 15), (0x1F5B, 0x1F5B, 2, 15), (0x1F5D, 0x1F5D, 2, 15), (0x1F5F, 0x1F7D, 2, 15), (0x1F80, 0x1FB4, 2, 15), (0x1FB6, 0x1FBC, 2, 15), (0x1FBD, 0x1FBD, 2, 9), (0x1FBE, 0x1FBE, 2, 15), (0x1FBF, 0x1FC1, 2, 9), (0x1FC2, 0x1FC4, 2, 15), (0x1FC6, 0x1FCC, 2, 15), (0x1FCD, 0x1FCF, 2, 9), (0x1FD0, 0x1FD3, 2, 15), (0x1FD6, 0x1FDB, 2, 15), (0x1FDD, 0x1FDF, 2, 9), (0x1FE0, 0x1FEC, 2, 15), (0x1FED, 0x1FEF, 2, 9), (0x1FF2, 0x1FF4, 2, 15), (0x1FF6, 0x1FFC, 2, 15), (0x1FFD, 0x1FFE, 2, 9), (0x2000, 0x200A, 0, 1), (0x200B, 0x200B, 0, 13), (0x200C, 0x200D, 40, 13), (0x200E, 0x200F, 0, 13), (0x2010, 0x2015, 0, 7), (0x2016, 0x2017, 0, 2), (0x2018, 0x2018, 0, 12), (0x2019, 0x2019, 0, 16), (0x201A, 0x201A, 0, 4), (0x201B, 0x201C, 0, 12), (0x201D, 0x201D, 0, 16), (0x201E, 0x201E, 0, 4), (0x201F, 0x201F, 0, 12), (0x2020, 0x2027, 0, 2), (0x2028, 0x2028, 0, 20), (0x2029, 0x2029, 0, 21), (0x202A, 0x202E, 0, 13), (0x202F, 0x202F, 0, 1), (0x2030, 0x2038, 0, 2), (0x2039, 0x2039, 0, 12), (0x203A, 0x203A, 0, 16), (0x203B, 0x203E, 0, 2), (0x203F, 0x2040, 0, 10), (0x2041, 0x2043, 0, 2), (0x2044, 0x2044, 0, 6), (0x2045, 0x2045, 0, 4), (0x2046, 0x2046, 0, 5), (0x2047, 0x2051, 0, 2), (0x2052, 0x2052, 0, 6), (0x2053, 0x2053, 0, 2), (0x2054, 0x2054, 0, 10), (0x2055, 0x205E, 0, 2), (0x205F, 0x205F, 0, 1), (0x2060, 0x2064, 0, 13), (0x206A, 0x206F, 0, 13), (0x2070, 0x2070, 0, 14), (0x2071, 0x2071, 1, 17), (0x2074, 0x2079, 0, 14), (0x207A, 0x207C, 0, 6), (0x207D, 0x207D, 0, 4), (0x207E, 0x207E, 0, 5), (0x207F, 0x207F, 1, 17), (0x2080, 0x2089, 0, 14), (0x208A, 0x208C, 0, 6), (0x208D, 0x208D, 0, 4), (0x208E, 0x208E, 0, 5), (0x2090, 0x209C, 1, 17), (0x20A0, 0x20B9, 0, 3), (0x20D0, 0x20DC, 40, 23), (0x20DD, 0x20E0, 40, 24), (0x20E1, 0x20E1, 40, 23), (0x20E2, 0x20E4, 40, 24), (0x20E5, 0x20F0, 40, 23), (0x2100, 0x2101, 0, 11), (0x2102, 0x2102, 0, 15), (0x2103, 0x2106, 0, 11), (0x2107, 0x2107, 0, 15), (0x2108, 0x2109, 0, 11), (0x210A, 0x2113, 0, 15), (0x2114, 0x2114, 0, 11), (0x2115, 0x2115, 0, 15), (0x2116, 0x2117, 0, 11), (0x2118, 0x2118, 0, 6), (0x2119, 0x211D, 0, 15), (0x211E, 0x2123, 0, 11), (0x2124, 0x2124, 0, 15), (0x2125, 0x2125, 0, 11), (0x2126, 0x2126, 2, 15), (0x2127, 0x2127, 0, 11), (0x2128, 0x2128, 0, 15), (0x2129, 0x2129, 0, 11), (0x212A, 0x212B, 1, 15), (0x212C, 0x212D, 0, 15), (0x212E, 0x212E, 0, 11), (0x212F, 0x2131, 0, 15), (0x2132, 0x2132, 1, 15), (0x2133, 0x2134, 0, 15), (0x2135, 0x2138, 0, 19), (0x2139, 0x2139, 0, 15), (0x213A, 0x213B, 0, 11), (0x213C, 0x213F, 0, 15), (0x2140, 0x2144, 0, 6), (0x2145, 0x2149, 0, 15), (0x214A, 0x214A, 0, 11), (0x214B, 0x214B, 0, 6), (0x214C, 0x214D, 0, 11), (0x214E, 0x214E, 1, 15), (0x214F, 0x214F, 0, 11), (0x2150, 0x215F, 0, 14), (0x2160, 0x2182, 1, 22), (0x2183, 0x2184, 1, 15), (0x2185, 0x2188, 1, 22), (0x2189, 0x2189, 0, 14), (0x2190, 0x2194, 0, 6), (0x2195, 0x2199, 0, 11), (0x219A, 0x219B, 0, 6), (0x219C, 0x219F, 0, 11), (0x21A0, 0x21A0, 0, 6), (0x21A1, 0x21A2, 0, 11), (0x21A3, 0x21A3, 0, 6), (0x21A4, 0x21A5, 0, 11), (0x21A6, 0x21A6, 0, 6), (0x21A7, 0x21AD, 0, 11), (0x21AE, 0x21AE, 0, 6), (0x21AF, 0x21CD, 0, 11), (0x21CE, 0x21CF, 0, 6), (0x21D0, 0x21D1, 0, 11), (0x21D2, 0x21D2, 0, 6), (0x21D3, 0x21D3, 0, 11), (0x21D4, 0x21D4, 0, 6), (0x21D5, 0x21F3, 0, 11), (0x21F4, 0x22FF, 0, 6), (0x2300, 0x2307, 0, 11), (0x2308, 0x230B, 0, 6), (0x230C, 0x231F, 0, 11), (0x2320, 0x2321, 0, 6), (0x2322, 0x2328, 0, 11), (0x2329, 0x2329, 0, 4), (0x232A, 0x232A, 0, 5), (0x232B, 0x237B, 0, 11), (0x237C, 0x237C, 0, 6), (0x237D, 0x239A, 0, 11), (0x239B, 0x23B3, 0, 6), (0x23B4, 0x23DB, 0, 11), (0x23DC, 0x23E1, 0, 6), (0x23E2, 0x23F3, 0, 11), (0x2400, 0x2426, 0, 11), (0x2440, 0x244A, 0, 11), (0x2460, 0x249B, 0, 14), (0x249C, 0x24E9, 0, 11), (0x24EA, 0x24FF, 0, 14), (0x2500, 0x25B6, 0, 11), (0x25B7, 0x25B7, 0, 6), (0x25B8, 0x25C0, 0, 11), (0x25C1, 0x25C1, 0, 6), (0x25C2, 0x25F7, 0, 11), (0x25F8, 0x25FF, 0, 6), (0x2600, 0x266E, 0, 11), (0x266F, 0x266F, 0, 6), (0x2670, 0x26FF, 0, 11), (0x2701, 0x2767, 0, 11), (0x2768, 0x2768, 0, 4), (0x2769, 0x2769, 0, 5), (0x276A, 0x276A, 0, 4), (0x276B, 0x276B, 0, 5), (0x276C, 0x276C, 0, 4), (0x276D, 0x276D, 0, 5), (0x276E, 0x276E, 0, 4), (0x276F, 0x276F, 0, 5), (0x2770, 0x2770, 0, 4), (0x2771, 0x2771, 0, 5), (0x2772, 0x2772, 0, 4), (0x2773, 0x2773, 0, 5), (0x2774, 0x2774, 0, 4), (0x2775, 0x2775, 0, 5), (0x2776, 0x2793, 0, 14), (0x2794, 0x27BF, 0, 11), (0x27C0, 0x27C4, 0, 6), (0x27C5, 0x27C5, 0, 4), (0x27C6, 0x27C6, 0, 5), (0x27C7, 0x27E5, 0, 6), (0x27E6, 0x27E6, 0, 4), (0x27E7, 0x27E7, 0, 5), (0x27E8, 0x27E8, 0, 4), (0x27E9, 0x27E9, 0, 5), (0x27EA, 0x27EA, 0, 4), (0x27EB, 0x27EB, 0, 5), (0x27EC, 0x27EC, 0, 4), (0x27ED, 0x27ED, 0, 5), (0x27EE, 0x27EE, 0, 4), (0x27EF, 0x27EF, 0, 5), (0x27F0, 0x27FF, 0, 6), (0x2800, 0x28FF, 52, 11), (0x2900, 0x2982, 0, 6), (0x2983, 0x2983, 0, 4), (0x2984, 0x2984, 0, 5), (0x2985, 0x2985, 0, 4), (0x2986, 0x2986, 0, 5), (0x2987, 0x2987, 0, 4), (0x2988, 0x2988, 0, 5), (0x2989, 0x2989, 0, 4), (0x298A, 0x298A, 0, 5), (0x298B, 0x298B, 0, 4), (0x298C, 0x298C, 0, 5), (0x298D, 0x298D, 0, 4), (0x298E, 0x298E, 0, 5), (0x298F, 0x298F, 0, 4), (0x2990, 0x2990, 0, 5), (0x2991, 0x2991, 0, 4), (0x2992, 0x2992, 0, 5), (0x2993, 0x2993, 0, 4), (0x2994, 0x2994, 0, 5), (0x2995, 0x2995, 0, 4), (0x2996, 0x2996, 0, 5), (0x2997, 0x2997, 0, 4), (0x2998, 0x2998, 0, 5), (0x2999, 0x29D7, 0, 6), (0x29D8, 0x29D8, 0, 4), (0x29D9, 0x29D9, 0, 5), (0x29DA, 0x29DA, 0, 4), (0x29DB, 0x29DB, 0, 5), (0x29DC, 0x29FB, 0, 6), (0x29FC, 0x29FC, 0, 4), (0x29FD, 0x29FD, 0, 5), (0x29FE, 0x2AFF, 0, 6), (0x2B00, 0x2B2F, 0, 11), (0x2B30, 0x2B44, 0, 6), (0x2B45, 0x2B46, 0, 11), (0x2B47, 0x2B4C, 0, 6), (0x2B50, 0x2B59, 0, 11), (0x2C00, 0x2C2E, 56, 15), (0x2C30, 0x2C5E, 56, 15), (0x2C60, 0x2C7B, 1, 15), (0x2C7C, 0x2C7D, 1, 17), (0x2C7E, 0x2C7F, 1, 15), (0x2C80, 0x2CE4, 54, 15), (0x2CE5, 0x2CEA, 54, 11), (0x2CEB, 0x2CEE, 54, 15), (0x2CEF, 0x2CF1, 54, 23), (0x2CF2, 0x2CF3, 54, 15), (0x2CF9, 0x2CFC, 54, 2), (0x2CFD, 0x2CFD, 54, 14), (0x2CFE, 0x2CFF, 54, 2), (0x2D00, 0x2D25, 23, 15), (0x2D27, 0x2D27, 23, 15), (0x2D2D, 0x2D2D, 23, 15), (0x2D30, 0x2D67, 57, 19), (0x2D6F, 0x2D6F, 57, 17), (0x2D70, 0x2D70, 57, 2), (0x2D7F, 0x2D7F, 57, 23), (0x2D80, 0x2D96, 25, 19), (0x2DA0, 0x2DA6, 25, 19), (0x2DA8, 0x2DAE, 25, 19), (0x2DB0, 0x2DB6, 25, 19), (0x2DB8, 0x2DBE, 25, 19), (0x2DC0, 0x2DC6, 25, 19), (0x2DC8, 0x2DCE, 25, 19), (0x2DD0, 0x2DD6, 25, 19), (0x2DD8, 0x2DDE, 25, 19), (0x2DE0, 0x2DFF, 3, 23), (0x2E00, 0x2E01, 0, 2), (0x2E02, 0x2E02, 0, 12), (0x2E03, 0x2E03, 0, 16), (0x2E04, 0x2E04, 0, 12), (0x2E05, 0x2E05, 0, 16), (0x2E06, 0x2E08, 0, 2), (0x2E09, 0x2E09, 0, 12), (0x2E0A, 0x2E0A, 0, 16), (0x2E0B, 0x2E0B, 0, 2), (0x2E0C, 0x2E0C, 0, 12), (0x2E0D, 0x2E0D, 0, 16), (0x2E0E, 0x2E16, 0, 2), (0x2E17, 0x2E17, 0, 7), (0x2E18, 0x2E19, 0, 2), (0x2E1A, 0x2E1A, 0, 7), (0x2E1B, 0x2E1B, 0, 2), (0x2E1C, 0x2E1C, 0, 12), (0x2E1D, 0x2E1D, 0, 16), (0x2E1E, 0x2E1F, 0, 2), (0x2E20, 0x2E20, 0, 12), (0x2E21, 0x2E21, 0, 16), (0x2E22, 0x2E22, 0, 4), (0x2E23, 0x2E23, 0, 5), (0x2E24, 0x2E24, 0, 4), (0x2E25, 0x2E25, 0, 5), (0x2E26, 0x2E26, 0, 4), (0x2E27, 0x2E27, 0, 5), (0x2E28, 0x2E28, 0, 4), (0x2E29, 0x2E29, 0, 5), (0x2E2A, 0x2E2E, 0, 2), (0x2E2F, 0x2E2F, 0, 17), (0x2E30, 0x2E39, 0, 2), (0x2E3A, 0x2E3B, 0, 7), (0x2E80, 0x2E99, 35, 11), (0x2E9B, 0x2EF3, 35, 11), (0x2F00, 0x2FD5, 35, 11), (0x2FF0, 0x2FFB, 0, 11), (0x3000, 0x3000, 0, 1), (0x3001, 0x3003, 0, 2), (0x3004, 0x3004, 0, 11), (0x3005, 0x3005, 35, 17), (0x3006, 0x3006, 0, 19), (0x3007, 0x3007, 35, 22), (0x3008, 0x3008, 0, 4), (0x3009, 0x3009, 0, 5), (0x300A, 0x300A, 0, 4), (0x300B, 0x300B, 0, 5), (0x300C, 0x300C, 0, 4), (0x300D, 0x300D, 0, 5), (0x300E, 0x300E, 0, 4), (0x300F, 0x300F, 0, 5), (0x3010, 0x3010, 0, 4), (0x3011, 0x3011, 0, 5), (0x3012, 0x3013, 0, 11), (0x3014, 0x3014, 0, 4), (0x3015, 0x3015, 0, 5), (0x3016, 0x3016, 0, 4), (0x3017, 0x3017, 0, 5), (0x3018, 0x3018, 0, 4), (0x3019, 0x3019, 0, 5), (0x301A, 0x301A, 0, 4), (0x301B, 0x301B, 0, 5), (0x301C, 0x301C, 0, 7), (0x301D, 0x301D, 0, 4), (0x301E, 0x301F, 0, 5), (0x3020, 0x3020, 0, 11), (0x3021, 0x3029, 35, 22), (0x302A, 0x302D, 40, 23), (0x302E, 0x302F, 24, 18), (0x3030, 0x3030, 0, 7), (0x3031, 0x3035, 0, 17), (0x3036, 0x3037, 0, 11), (0x3038, 0x303A, 35, 22), (0x303B, 0x303B, 35, 17), (0x303C, 0x303C, 0, 19), (0x303D, 0x303D, 0, 2), (0x303E, 0x303F, 0, 11), (0x3041, 0x3096, 32, 19), (0x3099, 0x309A, 40, 23), (0x309B, 0x309C, 0, 9), (0x309D, 0x309E, 32, 17), (0x309F, 0x309F, 32, 19), (0x30A0, 0x30A0, 0, 7), (0x30A1, 0x30FA, 33, 19), (0x30FB, 0x30FB, 0, 2), (0x30FC, 0x30FC, 0, 17), (0x30FD, 0x30FE, 33, 17), (0x30FF, 0x30FF, 33, 19), (0x3105, 0x312D, 34, 19), (0x3131, 0x318E, 24, 19), (0x3190, 0x3191, 0, 11), (0x3192, 0x3195, 0, 14), (0x3196, 0x319F, 0, 11), (0x31A0, 0x31BA, 34, 19), (0x31C0, 0x31E3, 0, 11), (0x31F0, 0x31FF, 33, 19), (0x3200, 0x321E, 24, 11), (0x3220, 0x3229, 0, 14), (0x322A, 0x3247, 0, 11), (0x3248, 0x324F, 0, 14), (0x3250, 0x3250, 0, 11), (0x3251, 0x325F, 0, 14), (0x3260, 0x327E, 24, 11), (0x327F, 0x327F, 0, 11), (0x3280, 0x3289, 0, 14), (0x328A, 0x32B0, 0, 11), (0x32B1, 0x32BF, 0, 14), (0x32C0, 0x32CF, 0, 11), (0x32D0, 0x32FE, 33, 11), (0x3300, 0x3357, 33, 11), (0x3358, 0x33FF, 0, 11), (0x3400, 0x4DB5, 35, 19), (0x4DC0, 0x4DFF, 0, 11), (0x4E00, 0x9FCC, 35, 19), (0xA000, 0xA014, 36, 19), (0xA015, 0xA015, 36, 17), (0xA016, 0xA48C, 36, 19), (0xA490, 0xA4C6, 36, 11), (0xA4D0, 0xA4F7, 82, 19), (0xA4F8, 0xA4FD, 82, 17), (0xA4FE, 0xA4FF, 82, 2), (0xA500, 0xA60B, 69, 19), (0xA60C, 0xA60C, 69, 17), (0xA60D, 0xA60F, 69, 2), (0xA610, 0xA61F, 69, 19), (0xA620, 0xA629, 69, 8), (0xA62A, 0xA62B, 69, 19), (0xA640, 0xA66D, 3, 15), (0xA66E, 0xA66E, 3, 19), (0xA66F, 0xA66F, 3, 23), (0xA670, 0xA672, 3, 24), (0xA673, 0xA673, 3, 2), (0xA674, 0xA67D, 3, 23), (0xA67E, 0xA67E, 3, 2), (0xA67F, 0xA67F, 3, 17), (0xA680, 0xA697, 3, 15), (0xA69F, 0xA69F, 3, 23), (0xA6A0, 0xA6E5, 83, 19), (0xA6E6, 0xA6EF, 83, 22), (0xA6F0, 0xA6F1, 83, 23), (0xA6F2, 0xA6F7, 83, 2), (0xA700, 0xA716, 0, 9), (0xA717, 0xA71F, 0, 17), (0xA720, 0xA721, 0, 9), (0xA722, 0xA76F, 1, 15), (0xA770, 0xA770, 1, 17), (0xA771, 0xA787, 1, 15), (0xA788, 0xA788, 0, 17), (0xA789, 0xA78A, 0, 9), (0xA78B, 0xA78E, 1, 15), (0xA790, 0xA793, 1, 15), (0xA7A0, 0xA7AA, 1, 15), (0xA7F8, 0xA7F9, 1, 17), (0xA7FA, 0xA7FA, 1, 15), (0xA7FB, 0xA7FF, 1, 19), (0xA800, 0xA801, 58, 19), (0xA802, 0xA802, 58, 23), (0xA803, 0xA805, 58, 19), (0xA806, 0xA806, 58, 23), (0xA807, 0xA80A, 58, 19), (0xA80B, 0xA80B, 58, 23), (0xA80C, 0xA822, 58, 19), (0xA823, 0xA824, 58, 18), (0xA825, 0xA826, 58, 23), (0xA827, 0xA827, 58, 18), (0xA828, 0xA82B, 58, 11), (0xA830, 0xA835, 0, 14), (0xA836, 0xA837, 0, 11), (0xA838, 0xA838, 0, 3), (0xA839, 0xA839, 0, 11), (0xA840, 0xA873, 64, 19), (0xA874, 0xA877, 64, 2), (0xA880, 0xA881, 70, 18), (0xA882, 0xA8B3, 70, 19), (0xA8B4, 0xA8C3, 70, 18), (0xA8C4, 0xA8C4, 70, 23), (0xA8CE, 0xA8CF, 70, 2), (0xA8D0, 0xA8D9, 70, 8), (0xA8E0, 0xA8F1, 9, 23), (0xA8F2, 0xA8F7, 9, 19), (0xA8F8, 0xA8FA, 9, 2), (0xA8FB, 0xA8FB, 9, 19), (0xA900, 0xA909, 71, 8), (0xA90A, 0xA925, 71, 19), (0xA926, 0xA92D, 71, 23), (0xA92E, 0xA92F, 71, 2), (0xA930, 0xA946, 72, 19), (0xA947, 0xA951, 72, 23), (0xA952, 0xA953, 72, 18), (0xA95F, 0xA95F, 72, 2), (0xA960, 0xA97C, 24, 19), (0xA980, 0xA982, 84, 23), (0xA983, 0xA983, 84, 18), (0xA984, 0xA9B2, 84, 19), (0xA9B3, 0xA9B3, 84, 23), (0xA9B4, 0xA9B5, 84, 18), (0xA9B6, 0xA9B9, 84, 23), (0xA9BA, 0xA9BB, 84, 18), (0xA9BC, 0xA9BC, 84, 23), (0xA9BD, 0xA9C0, 84, 18), (0xA9C1, 0xA9CD, 84, 2), (0xA9CF, 0xA9CF, 84, 17), (0xA9D0, 0xA9D9, 84, 8), (0xA9DE, 0xA9DF, 84, 2), (0xAA00, 0xAA28, 76, 19), (0xAA29, 0xAA2E, 76, 23), (0xAA2F, 0xAA30, 76, 18), (0xAA31, 0xAA32, 76, 23), (0xAA33, 0xAA34, 76, 18), (0xAA35, 0xAA36, 76, 23), (0xAA40, 0xAA42, 76, 19), (0xAA43, 0xAA43, 76, 23), (0xAA44, 0xAA4B, 76, 19), (0xAA4C, 0xAA4C, 76, 23), (0xAA4D, 0xAA4D, 76, 18), (0xAA50, 0xAA59, 76, 8), (0xAA5C, 0xAA5F, 76, 2), (0xAA60, 0xAA6F, 22, 19), (0xAA70, 0xAA70, 22, 17), (0xAA71, 0xAA76, 22, 19), (0xAA77, 0xAA79, 22, 11), (0xAA7A, 0xAA7A, 22, 19), (0xAA7B, 0xAA7B, 22, 18), (0xAA80, 0xAAAF, 78, 19), (0xAAB0, 0xAAB0, 78, 23), (0xAAB1, 0xAAB1, 78, 19), (0xAAB2, 0xAAB4, 78, 23), (0xAAB5, 0xAAB6, 78, 19), (0xAAB7, 0xAAB8, 78, 23), (0xAAB9, 0xAABD, 78, 19), (0xAABE, 0xAABF, 78, 23), (0xAAC0, 0xAAC0, 78, 19), (0xAAC1, 0xAAC1, 78, 23), (0xAAC2, 0xAAC2, 78, 19), (0xAADB, 0xAADC, 78, 19), (0xAADD, 0xAADD, 78, 17), (0xAADE, 0xAADF, 78, 2), (0xAAE0, 0xAAEA, 85, 19), (0xAAEB, 0xAAEB, 85, 18), (0xAAEC, 0xAAED, 85, 23), (0xAAEE, 0xAAEF, 85, 18), (0xAAF0, 0xAAF1, 85, 2), (0xAAF2, 0xAAF2, 85, 19), (0xAAF3, 0xAAF4, 85, 17), (0xAAF5, 0xAAF5, 85, 18), (0xAAF6, 0xAAF6, 85, 23), (0xAB01, 0xAB06, 25, 19), (0xAB09, 0xAB0E, 25, 19), (0xAB11, 0xAB16, 25, 19), (0xAB20, 0xAB26, 25, 19), (0xAB28, 0xAB2E, 25, 19), (0xABC0, 0xABE2, 85, 19), (0xABE3, 0xABE4, 85, 18), (0xABE5, 0xABE5, 85, 23), (0xABE6, 0xABE7, 85, 18), (0xABE8, 0xABE8, 85, 23), (0xABE9, 0xABEA, 85, 18), (0xABEB, 0xABEB, 85, 2), (0xABEC, 0xABEC, 85, 18), (0xABED, 0xABED, 85, 23), (0xABF0, 0xABF9, 85, 8), (0xAC00, 0xD7A3, 24, 19), (0xD7B0, 0xD7C6, 24, 19), (0xD7CB, 0xD7FB, 24, 19), (0xF900, 0xFA6D, 35, 19), (0xFA70, 0xFAD9, 35, 19), (0xFB00, 0xFB06, 1, 15), (0xFB13, 0xFB17, 4, 15), (0xFB1D, 0xFB1D, 5, 19), (0xFB1E, 0xFB1E, 5, 23), (0xFB1F, 0xFB28, 5, 19), (0xFB29, 0xFB29, 5, 6), (0xFB2A, 0xFB36, 5, 19), (0xFB38, 0xFB3C, 5, 19), (0xFB3E, 0xFB3E, 5, 19), (0xFB40, 0xFB41, 5, 19), (0xFB43, 0xFB44, 5, 19), (0xFB46, 0xFB4F, 5, 19), (0xFB50, 0xFBB1, 6, 19), (0xFBB2, 0xFBC1, 6, 9), (0xFBD3, 0xFD3D, 6, 19), (0xFD3E, 0xFD3E, 0, 4), (0xFD3F, 0xFD3F, 0, 5), (0xFD50, 0xFD8F, 6, 19), (0xFD92, 0xFDC7, 6, 19), (0xFDF0, 0xFDFB, 6, 19), (0xFDFC, 0xFDFC, 6, 3), (0xFDFD, 0xFDFD, 0, 11), (0xFE00, 0xFE0F, 40, 23), (0xFE10, 0xFE16, 0, 2), (0xFE17, 0xFE17, 0, 4), (0xFE18, 0xFE18, 0, 5), (0xFE19, 0xFE19, 0, 2), (0xFE20, 0xFE26, 40, 23), (0xFE30, 0xFE30, 0, 2), (0xFE31, 0xFE32, 0, 7), (0xFE33, 0xFE34, 0, 10), (0xFE35, 0xFE35, 0, 4), (0xFE36, 0xFE36, 0, 5), (0xFE37, 0xFE37, 0, 4), (0xFE38, 0xFE38, 0, 5), (0xFE39, 0xFE39, 0, 4), (0xFE3A, 0xFE3A, 0, 5), (0xFE3B, 0xFE3B, 0, 4), (0xFE3C, 0xFE3C, 0, 5), (0xFE3D, 0xFE3D, 0, 4), (0xFE3E, 0xFE3E, 0, 5), (0xFE3F, 0xFE3F, 0, 4), (0xFE40, 0xFE40, 0, 5), (0xFE41, 0xFE41, 0, 4), (0xFE42, 0xFE42, 0, 5), (0xFE43, 0xFE43, 0, 4), (0xFE44, 0xFE44, 0, 5), (0xFE45, 0xFE46, 0, 2), (0xFE47, 0xFE47, 0, 4), (0xFE48, 0xFE48, 0, 5), (0xFE49, 0xFE4C, 0, 2), (0xFE4D, 0xFE4F, 0, 10), (0xFE50, 0xFE52, 0, 2), (0xFE54, 0xFE57, 0, 2), (0xFE58, 0xFE58, 0, 7), (0xFE59, 0xFE59, 0, 4), (0xFE5A, 0xFE5A, 0, 5), (0xFE5B, 0xFE5B, 0, 4), (0xFE5C, 0xFE5C, 0, 5), (0xFE5D, 0xFE5D, 0, 4), (0xFE5E, 0xFE5E, 0, 5), (0xFE5F, 0xFE61, 0, 2), (0xFE62, 0xFE62, 0, 6), (0xFE63, 0xFE63, 0, 7), (0xFE64, 0xFE66, 0, 6), (0xFE68, 0xFE68, 0, 2), (0xFE69, 0xFE69, 0, 3), (0xFE6A, 0xFE6B, 0, 2), (0xFE70, 0xFE74, 6, 19), (0xFE76, 0xFEFC, 6, 19), (0xFEFF, 0xFEFF, 0, 13), (0xFF01, 0xFF03, 0, 2), (0xFF04, 0xFF04, 0, 3), (0xFF05, 0xFF07, 0, 2), (0xFF08, 0xFF08, 0, 4), (0xFF09, 0xFF09, 0, 5), (0xFF0A, 0xFF0A, 0, 2), (0xFF0B, 0xFF0B, 0, 6), (0xFF0C, 0xFF0C, 0, 2), (0xFF0D, 0xFF0D, 0, 7), (0xFF0E, 0xFF0F, 0, 2), (0xFF10, 0xFF19, 0, 8), (0xFF1A, 0xFF1B, 0, 2), (0xFF1C, 0xFF1E, 0, 6), (0xFF1F, 0xFF20, 0, 2), (0xFF21, 0xFF3A, 1, 15), (0xFF3B, 0xFF3B, 0, 4), (0xFF3C, 0xFF3C, 0, 2), (0xFF3D, 0xFF3D, 0, 5), (0xFF3E, 0xFF3E, 0, 9), (0xFF3F, 0xFF3F, 0, 10), (0xFF40, 0xFF40, 0, 9), (0xFF41, 0xFF5A, 1, 15), (0xFF5B, 0xFF5B, 0, 4), (0xFF5C, 0xFF5C, 0, 6), (0xFF5D, 0xFF5D, 0, 5), (0xFF5E, 0xFF5E, 0, 6), (0xFF5F, 0xFF5F, 0, 4), (0xFF60, 0xFF60, 0, 5), (0xFF61, 0xFF61, 0, 2), (0xFF62, 0xFF62, 0, 4), (0xFF63, 0xFF63, 0, 5), (0xFF64, 0xFF65, 0, 2), (0xFF66, 0xFF6F, 33, 19), (0xFF70, 0xFF70, 0, 17), (0xFF71, 0xFF9D, 33, 19), (0xFF9E, 0xFF9F, 0, 17), (0xFFA0, 0xFFBE, 24, 19), (0xFFC2, 0xFFC7, 24, 19), (0xFFCA, 0xFFCF, 24, 19), (0xFFD2, 0xFFD7, 24, 19), (0xFFDA, 0xFFDC, 24, 19), (0xFFE0, 0xFFE1, 0, 3), (0xFFE2, 0xFFE2, 0, 6), (0xFFE3, 0xFFE3, 0, 9), (0xFFE4, 0xFFE4, 0, 11), (0xFFE5, 0xFFE6, 0, 3), (0xFFE8, 0xFFE8, 0, 11), (0xFFE9, 0xFFEC, 0, 6), (0xFFED, 0xFFEE, 0, 11), (0xFFF9, 0xFFFB, 0, 13), (0xFFFC, 0xFFFD, 0, 11), (0x10000, 0x1000B, 47, 19), (0x1000D, 0x10026, 47, 19), (0x10028, 0x1003A, 47, 19), (0x1003C, 0x1003D, 47, 19), (0x1003F, 0x1004D, 47, 19), (0x10050, 0x1005D, 47, 19), (0x10080, 0x100FA, 47, 19), (0x10100, 0x10102, 0, 2), (0x10107, 0x10133, 0, 14), (0x10137, 0x1013F, 0, 11), (0x10140, 0x10174, 2, 22), (0x10175, 0x10178, 2, 14), (0x10179, 0x10189, 2, 11), (0x1018A, 0x1018A, 2, 14), (0x10190, 0x1019B, 0, 11), (0x101D0, 0x101FC, 0, 11), (0x101FD, 0x101FD, 40, 23), (0x10280, 0x1029C, 73, 19), (0x102A0, 0x102D0, 74, 19), (0x10300, 0x1031E, 37, 19), (0x10320, 0x10323, 37, 14), (0x10330, 0x10340, 38, 19), (0x10341, 0x10341, 38, 22), (0x10342, 0x10349, 38, 19), (0x1034A, 0x1034A, 38, 22), (0x10380, 0x1039D, 48, 19), (0x1039F, 0x1039F, 48, 2), (0x103A0, 0x103C3, 59, 19), (0x103C8, 0x103CF, 59, 19), (0x103D0, 0x103D0, 59, 2), (0x103D1, 0x103D5, 59, 22), (0x10400, 0x1044F, 39, 15), (0x10450, 0x1047F, 49, 19), (0x10480, 0x1049D, 50, 19), (0x104A0, 0x104A9, 50, 8), (0x10800, 0x10805, 51, 19), (0x10808, 0x10808, 51, 19), (0x1080A, 0x10835, 51, 19), (0x10837, 0x10838, 51, 19), (0x1083C, 0x1083C, 51, 19), (0x1083F, 0x1083F, 51, 19), (0x10840, 0x10855, 86, 19), (0x10857, 0x10857, 86, 2), (0x10858, 0x1085F, 86, 14), (0x10900, 0x10915, 63, 19), (0x10916, 0x1091B, 63, 14), (0x1091F, 0x1091F, 63, 2), (0x10920, 0x10939, 75, 19), (0x1093F, 0x1093F, 75, 2), (0x10980, 0x1099F, 97, 19), (0x109A0, 0x109B7, 96, 19), (0x109BE, 0x109BF, 96, 19), (0x10A00, 0x10A00, 60, 19), (0x10A01, 0x10A03, 60, 23), (0x10A05, 0x10A06, 60, 23), (0x10A0C, 0x10A0F, 60, 23), (0x10A10, 0x10A13, 60, 19), (0x10A15, 0x10A17, 60, 19), (0x10A19, 0x10A33, 60, 19), (0x10A38, 0x10A3A, 60, 23), (0x10A3F, 0x10A3F, 60, 23), (0x10A40, 0x10A47, 60, 14), (0x10A50, 0x10A58, 60, 2), (0x10A60, 0x10A7C, 87, 19), (0x10A7D, 0x10A7E, 87, 14), (0x10A7F, 0x10A7F, 87, 2), (0x10B00, 0x10B35, 79, 19), (0x10B39, 0x10B3F, 79, 2), (0x10B40, 0x10B55, 88, 19), (0x10B58, 0x10B5F, 88, 14), (0x10B60, 0x10B72, 89, 19), (0x10B78, 0x10B7F, 89, 14), (0x10C00, 0x10C48, 90, 19), (0x10E60, 0x10E7E, 6, 14), (0x11000, 0x11000, 93, 18), (0x11001, 0x11001, 93, 23), (0x11002, 0x11002, 93, 18), (0x11003, 0x11037, 93, 19), (0x11038, 0x11046, 93, 23), (0x11047, 0x1104D, 93, 2), (0x11052, 0x11065, 93, 14), (0x11066, 0x1106F, 93, 8), (0x11080, 0x11081, 91, 23), (0x11082, 0x11082, 91, 18), (0x11083, 0x110AF, 91, 19), (0x110B0, 0x110B2, 91, 18), (0x110B3, 0x110B6, 91, 23), (0x110B7, 0x110B8, 91, 18), (0x110B9, 0x110BA, 91, 23), (0x110BB, 0x110BC, 91, 2), (0x110BD, 0x110BD, 91, 13), (0x110BE, 0x110C1, 91, 2), (0x110D0, 0x110E8, 100, 19), (0x110F0, 0x110F9, 100, 8), (0x11100, 0x11102, 95, 23), (0x11103, 0x11126, 95, 19), (0x11127, 0x1112B, 95, 23), (0x1112C, 0x1112C, 95, 18), (0x1112D, 0x11134, 95, 23), (0x11136, 0x1113F, 95, 8), (0x11140, 0x11143, 95, 2), (0x11180, 0x11181, 99, 23), (0x11182, 0x11182, 99, 18), (0x11183, 0x111B2, 99, 19), (0x111B3, 0x111B5, 99, 18), (0x111B6, 0x111BE, 99, 23), (0x111BF, 0x111C0, 99, 18), (0x111C1, 0x111C4, 99, 19), (0x111C5, 0x111C8, 99, 2), (0x111D0, 0x111D9, 99, 8), (0x11680, 0x116AA, 101, 19), (0x116AB, 0x116AB, 101, 23), (0x116AC, 0x116AC, 101, 18), (0x116AD, 0x116AD, 101, 23), (0x116AE, 0x116AF, 101, 18), (0x116B0, 0x116B5, 101, 23), (0x116B6, 0x116B6, 101, 18), (0x116B7, 0x116B7, 101, 23), (0x116C0, 0x116C9, 101, 8), (0x12000, 0x1236E, 62, 19), (0x12400, 0x12462, 62, 22), (0x12470, 0x12473, 62, 2), (0x13000, 0x1342E, 80, 19), (0x16800, 0x16A38, 83, 19), (0x16F00, 0x16F44, 98, 19), (0x16F50, 0x16F50, 98, 19), (0x16F51, 0x16F7E, 98, 18), (0x16F8F, 0x16F92, 98, 23), (0x16F93, 0x16F9F, 98, 17), (0x1B000, 0x1B000, 33, 19), (0x1B001, 0x1B001, 32, 19), (0x1D000, 0x1D0F5, 0, 11), (0x1D100, 0x1D126, 0, 11), (0x1D129, 0x1D164, 0, 11), (0x1D165, 0x1D166, 0, 18), (0x1D167, 0x1D169, 40, 23), (0x1D16A, 0x1D16C, 0, 11), (0x1D16D, 0x1D172, 0, 18), (0x1D173, 0x1D17A, 0, 13), (0x1D17B, 0x1D182, 40, 23), (0x1D183, 0x1D184, 0, 11), (0x1D185, 0x1D18B, 40, 23), (0x1D18C, 0x1D1A9, 0, 11), (0x1D1AA, 0x1D1AD, 40, 23), (0x1D1AE, 0x1D1DD, 0, 11), (0x1D200, 0x1D241, 2, 11), (0x1D242, 0x1D244, 2, 23), (0x1D245, 0x1D245, 2, 11), (0x1D300, 0x1D356, 0, 11), (0x1D360, 0x1D371, 0, 14), (0x1D400, 0x1D454, 0, 15), (0x1D456, 0x1D49C, 0, 15), (0x1D49E, 0x1D49F, 0, 15), (0x1D4A2, 0x1D4A2, 0, 15), (0x1D4A5, 0x1D4A6, 0, 15), (0x1D4A9, 0x1D4AC, 0, 15), (0x1D4AE, 0x1D4B9, 0, 15), (0x1D4BB, 0x1D4BB, 0, 15), (0x1D4BD, 0x1D4C3, 0, 15), (0x1D4C5, 0x1D505, 0, 15), (0x1D507, 0x1D50A, 0, 15), (0x1D50D, 0x1D514, 0, 15), (0x1D516, 0x1D51C, 0, 15), (0x1D51E, 0x1D539, 0, 15), (0x1D53B, 0x1D53E, 0, 15), (0x1D540, 0x1D544, 0, 15), (0x1D546, 0x1D546, 0, 15), (0x1D54A, 0x1D550, 0, 15), (0x1D552, 0x1D6A5, 0, 15), (0x1D6A8, 0x1D6C0, 0, 15), (0x1D6C1, 0x1D6C1, 0, 6), (0x1D6C2, 0x1D6DA, 0, 15), (0x1D6DB, 0x1D6DB, 0, 6), (0x1D6DC, 0x1D6FA, 0, 15), (0x1D6FB, 0x1D6FB, 0, 6), (0x1D6FC, 0x1D714, 0, 15), (0x1D715, 0x1D715, 0, 6), (0x1D716, 0x1D734, 0, 15), (0x1D735, 0x1D735, 0, 6), (0x1D736, 0x1D74E, 0, 15), (0x1D74F, 0x1D74F, 0, 6), (0x1D750, 0x1D76E, 0, 15), (0x1D76F, 0x1D76F, 0, 6), (0x1D770, 0x1D788, 0, 15), (0x1D789, 0x1D789, 0, 6), (0x1D78A, 0x1D7A8, 0, 15), (0x1D7A9, 0x1D7A9, 0, 6), (0x1D7AA, 0x1D7C2, 0, 15), (0x1D7C3, 0x1D7C3, 0, 6), (0x1D7C4, 0x1D7CB, 0, 15), (0x1D7CE, 0x1D7FF, 0, 8), (0x1EE00, 0x1EE03, 6, 19), (0x1EE05, 0x1EE1F, 6, 19), (0x1EE21, 0x1EE22, 6, 19), (0x1EE24, 0x1EE24, 6, 19), (0x1EE27, 0x1EE27, 6, 19), (0x1EE29, 0x1EE32, 6, 19), (0x1EE34, 0x1EE37, 6, 19), (0x1EE39, 0x1EE39, 6, 19), (0x1EE3B, 0x1EE3B, 6, 19), (0x1EE42, 0x1EE42, 6, 19), (0x1EE47, 0x1EE47, 6, 19), (0x1EE49, 0x1EE49, 6, 19), (0x1EE4B, 0x1EE4B, 6, 19), (0x1EE4D, 0x1EE4F, 6, 19), (0x1EE51, 0x1EE52, 6, 19), (0x1EE54, 0x1EE54, 6, 19), (0x1EE57, 0x1EE57, 6, 19), (0x1EE59, 0x1EE59, 6, 19), (0x1EE5B, 0x1EE5B, 6, 19), (0x1EE5D, 0x1EE5D, 6, 19), (0x1EE5F, 0x1EE5F, 6, 19), (0x1EE61, 0x1EE62, 6, 19), (0x1EE64, 0x1EE64, 6, 19), (0x1EE67, 0x1EE6A, 6, 19), (0x1EE6C, 0x1EE72, 6, 19), (0x1EE74, 0x1EE77, 6, 19), (0x1EE79, 0x1EE7C, 6, 19), (0x1EE7E, 0x1EE7E, 6, 19), (0x1EE80, 0x1EE89, 6, 19), (0x1EE8B, 0x1EE9B, 6, 19), (0x1EEA1, 0x1EEA3, 6, 19), (0x1EEA5, 0x1EEA9, 6, 19), (0x1EEAB, 0x1EEBB, 6, 19), (0x1EEF0, 0x1EEF1, 6, 6), (0x1F000, 0x1F02B, 0, 11), (0x1F030, 0x1F093, 0, 11), (0x1F0A0, 0x1F0AE, 0, 11), (0x1F0B1, 0x1F0BE, 0, 11), (0x1F0C1, 0x1F0CF, 0, 11), (0x1F0D1, 0x1F0DF, 0, 11), (0x1F100, 0x1F10A, 0, 14), (0x1F110, 0x1F12E, 0, 11), (0x1F130, 0x1F16B, 0, 11), (0x1F170, 0x1F19A, 0, 11), (0x1F1E6, 0x1F1FF, 0, 11), (0x1F200, 0x1F200, 32, 11), (0x1F201, 0x1F202, 0, 11), (0x1F210, 0x1F23A, 0, 11), (0x1F240, 0x1F248, 0, 11), (0x1F250, 0x1F251, 0, 11), (0x1F300, 0x1F320, 0, 11), (0x1F330, 0x1F335, 0, 11), (0x1F337, 0x1F37C, 0, 11), (0x1F380, 0x1F393, 0, 11), (0x1F3A0, 0x1F3C4, 0, 11), (0x1F3C6, 0x1F3CA, 0, 11), (0x1F3E0, 0x1F3F0, 0, 11), (0x1F400, 0x1F43E, 0, 11), (0x1F440, 0x1F440, 0, 11), (0x1F442, 0x1F4F7, 0, 11), (0x1F4F9, 0x1F4FC, 0, 11), (0x1F500, 0x1F53D, 0, 11), (0x1F540, 0x1F543, 0, 11), (0x1F550, 0x1F567, 0, 11), (0x1F5FB, 0x1F640, 0, 11), (0x1F645, 0x1F64F, 0, 11), (0x1F680, 0x1F6C5, 0, 11), (0x1F700, 0x1F773, 0, 11), (0x20000, 0x2A6D6, 35, 19), (0x2A700, 0x2B734, 35, 19), (0x2B740, 0x2B81D, 35, 19), (0x2F800, 0x2FA1D, 35, 19), (0xE0001, 0xE0001, 0, 13), (0xE0020, 0xE007F, 0, 13), (0xE0100, 0xE01EF, 40, 23), ], } def script_cat(chr): """For the unicode character chr return a tuple (Scriptname, Category).""" l = 0 r = len(script_data["idx"]) - 1 c = ord(chr) while r >= l: m = (l + r) >> 1 if c < script_data["idx"][m][0]: r = m - 1 elif c > script_data["idx"][m][1]: l = m + 1 else: return ( script_data["names"][script_data["idx"][m][2]], script_data["cats"][script_data["idx"][m][3]], ) return "Unknown", "Zzzz" def script(chr): a, _ = script_cat(chr) return a def category(chr): _, a = script_cat(chr) return a def _compile_scripts_txt(): # build indexes from 'scripts.txt' idx = [] names = [] cats = [] import urllib2, re, textwrap url = "http://www.unicode.org/Public/UNIDATA/Scripts.txt" f = urllib2.urlopen(url) for ln in f: p = re.findall(r"([0-9A-F]+)(?:\.\.([0-9A-F]+))?\W+(\w+)\s*#\s*(\w+)", ln) if p: a, b, name, cat = p[0] if name not in names: names.append(name) if cat not in cats: cats.append(cat) idx.append( (int(a, 16), int(b or a, 16), names.index(name), cats.index(cat)) ) idx.sort() print( 'script_data = {\n"names":%s,\n"cats":%s,\n"idx":[\n%s\n]}' % ( "\n".join(textwrap.wrap(repr(names), 80)), "\n".join(textwrap.wrap(repr(cats), 80)), "\n".join( textwrap.wrap(", ".join("(0x%x,0x%x,%d,%d)" % c for c in idx), 80) ), ) ) ================================================ FILE: lib/globals.py ================================================ """ global configuration of the project External environment variables: WTTR_MYDIR WTTR_GEOLITE WTTR_WEGO WTTR_LISTEN_HOST WTTR_LISTEN_PORT WTTR_USER_AGENT """ from __future__ import print_function import logging import os import re MYDIR = os.path.abspath(os.path.dirname(os.path.dirname("__file__"))) if "WTTR_GEOLITE" in os.environ: GEOLITE = os.environ["WTTR_GEOLITE"] else: GEOLITE = os.path.join(MYDIR, "data", "GeoLite2-City.mmdb") WEGO = os.environ.get("WTTR_WEGO", "/home/igor/go/bin/we-lang") PYPHOON = "/home/igor/src/pyphoon/ve/bin/pyphoon-lolcat" # "pyphoon-lolcat" _DATADIR = "/wttr.in" _LOGDIR = "/wttr.in/log" IP2LCACHE = os.path.join(_DATADIR, "cache/ip2l/") PNG_CACHE = os.path.join(_DATADIR, "cache/png") LRU_CACHE = os.path.join(_DATADIR, "cache/lru") LOG_FILE = os.path.join(_LOGDIR, "main.log") PROXY_LOG_ACCESS = os.path.join(_LOGDIR, "proxy-access.log") PROXY_LOG_ERRORS = os.path.join(_LOGDIR, "proxy-errors.log") MISSING_TRANSLATION_LOG = os.path.join(_LOGDIR, "missing-translation/%s.log") ALIASES = os.path.join(MYDIR, "share/aliases") ANSI2HTML = os.path.join(MYDIR, "share/ansi2html.sh") BLACKLIST = os.path.join(MYDIR, "share/blacklist") HELP_FILE = os.path.join(MYDIR, "share/help.txt") BASH_FUNCTION_FILE = os.path.join(MYDIR, "share/bash-function.txt") TRANSLATION_FILE = os.path.join(MYDIR, "share/translation.txt") IATA_CODES_FILE = os.path.join(MYDIR, "share/list-of-iata-codes.txt") TEMPLATES = os.path.join(MYDIR, "share/templates") STATIC = os.path.join(MYDIR, "share/static") NOT_FOUND_LOCATION = "not found" DEFAULT_LOCATION = "oymyakon" MALFORMED_RESPONSE_HTML_PAGE = open( os.path.join(STATIC, "malformed-response.html") ).read() GEOLOCATOR_SERVICE = "http://localhost:8004" # number of queries from the same IP address is limited # (minute, hour, day) limitations: QUERY_LIMITS = (300, 3600, 24 * 3600) LISTEN_HOST = os.environ.get("WTTR_LISTEN_HOST", "") try: LISTEN_PORT = int(os.environ.get("WTTR_LISTEN_PORT")) except (TypeError, ValueError): LISTEN_PORT = 8002 PROXY_HOST = "127.0.0.1" PROXY_PORT = 5001 PROXY_CACHEDIR = os.path.join(_DATADIR, "cache/proxy-wwo/") MY_EXTERNAL_IP = "5.9.243.187" PLAIN_TEXT_AGENTS = [ "curl", "httpie", "lwp-request", "wget", "python-requests", "python-httpx", "openbsd ftp", "powershell", "fetch", "aiohttp", "http_get", "xh", "nushell", "zig", ] PLAIN_TEXT_PAGES = [":help", ":bash.function", ":translation", ":iterm2"] TRANSLATION_TABLE = str.maketrans( { "\u2196": "\u256E", # '↖' -> '╮' "\u2197": "\u256D", # '↗' -> '╭' "\u2198": "\u2570", # '↘' -> '╰' "\u2199": "\u256F", # '↙' -> '╯' "\u26A1": "\u250C\u2518", } ) _IPLOCATION_ORDER = os.environ.get("WTTR_IPLOCATION_ORDER", "geoip,ip2location,ipinfo") IPLOCATION_ORDER = _IPLOCATION_ORDER.split(",") _IP2LOCATION_KEY_FILE = os.environ.get( "WTTR_IP2LOCATION_KEY_FILE", os.environ["HOME"] + "/.ip2location.key" ) IP2LOCATION_KEY = None if os.path.exists(_IP2LOCATION_KEY_FILE): IP2LOCATION_KEY = open(_IP2LOCATION_KEY_FILE, "r").read().strip() _IPINFO_KEY_FILE = os.environ.get( "WTTR_IPINFO_KEY_FILE", os.environ["HOME"] + "/.ipinfo.key" ) IPINFO_TOKEN = None if os.path.exists(_IPINFO_KEY_FILE): IPINFO_TOKEN = open(_IPINFO_KEY_FILE, "r").read().strip() _WWO_KEY_FILE = os.environ.get("WTTR_WWO_KEY_FILE", os.environ["HOME"] + "/.wwo.key") WWO_KEY = "key-is-not-specified" USE_METNO = True USER_AGENT = os.environ.get("WTTR_USER_AGENT", "") if os.path.exists(_WWO_KEY_FILE): WWO_KEY = open(_WWO_KEY_FILE, "r").read().strip() USE_METNO = False def error(text): "log error `text` and raise a RuntimeError exception" if not text.startswith("Too many queries"): print(text) logging.error("ERROR %s", text) raise RuntimeError(text) def log(text): "log error `text` and do not raise any exceptions" if not text.startswith("Too many queries"): print(text) logging.info(text) def debug_log(text): """ Write `text` to the debug log """ with open("/tmp/wttr.in-debug.log", "a") as f_debug: f_debug.write(text + "\n") def get_help_file(lang): "Return help file for `lang`" help_file = os.path.join(MYDIR, "share/translations/%s-help.txt" % lang) if os.path.exists(help_file): return help_file return HELP_FILE def remove_ansi(sometext): ansi_escape = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") return ansi_escape.sub("", sometext) ================================================ FILE: lib/limits.py ================================================ """ Connection limitation. Number of connections from one IP is limited. We have nothing against scripting and automated queries. Even the opposite, we encourage them. But there are some connection limits that even we can't handle. Currently the limits are quite restrictive, but they will be relaxed in the future. Usage: limits = Limits() not_allowed = limits.check_ip(ip_address) if not_allowed: return "ERROR: %s" % not_allowed [Taken from github.com/chubin/cheat.sh] """ import time from globals import log def _time_caps(minutes, hours, days): return { "min": minutes, "hour": hours, "day": days, } class Limits(object): """ Queries limitation (by IP). Exports: check_ip(ip_address) """ def __init__(self, whitelist=None, limits=None): self.intervals = ["min", "hour", "day"] self.divisor = _time_caps(60, 3600, 86400) self.last_update = _time_caps(0, 0, 0) if limits: self.limit = _time_caps(*limits) else: self.limit = _time_caps(30, 600, 1000) if whitelist: self.whitelist = whitelist[:] else: self.whitelist = [] self.counter = { "min": {}, "hour": {}, "day": {}, } self._clear_counters_if_needed() def _log_visit(self, interval, ip_address): if ip_address not in self.counter[interval]: self.counter[interval][ip_address] = 0 self.counter[interval][ip_address] += 1 def _limit_exceeded(self, interval, ip_address): visits = self.counter[interval][ip_address] limit = self._get_limit(interval) return visits > limit def _get_limit(self, interval): return self.limit[interval] def _report_excessive_visits(self, interval, ip_address): log( "%s LIMITED [%s for %s]" % (ip_address, self._get_limit(interval), interval) ) def check_ip(self, ip_address): """ Check if `ip_address` is allowed, and if not raise an RuntimeError exception. Return True otherwise """ if ip_address in self.whitelist: return None self._clear_counters_if_needed() for interval in self.intervals: self._log_visit(interval, ip_address) if self._limit_exceeded(interval, ip_address): self._report_excessive_visits(interval, ip_address) return "Not so fast! Number of queries per %s is limited to %s" % ( interval, self._get_limit(interval), ) return None def reset(self): """ Reset all counters for all IPs """ for interval in self.intervals: self.counter[interval] = {} def _clear_counters_if_needed(self): current_time = int(time.time()) for interval in self.intervals: if current_time // self.divisor[interval] != self.last_update[interval]: self.counter[interval] = {} self.last_update[interval] = current_time / self.divisor[interval] ================================================ FILE: lib/location.py ================================================ """ All location related functions and converters. The main entry point is `location_processing` which gets `location` and `source_ip_address` and basing on this information generates precise location description. [query] --> [location] --> [(lat,long)] --> ^ | [ip-address] --> _get_location() To resolve IP address into location, the module uses function `_get_location()`, which subsequenlty utilizes one of the three methods: * `_geoip2()` (local sqlite database); * `_ip2location()` (an external paid service); * `_ipinfo()` (an external free service). IP-address resolution data is saved in cache (_ipcachewrite, _ipcache). Cache entry format: COUNTRY_CODE;COUNTRY;REGION;CITY[;REST] To resolve location name into a (lat,long) pair, an external service is used, which is wrapped with a function `_geolocator()`. Exports: location_processing is_location_blocked """ from __future__ import print_function import datetime import json import os import socket import sys import random import geoip2.database import pycountry import requests from globals import ( GEOLITE, GEOLOCATOR_SERVICE, IP2LCACHE, IP2LOCATION_KEY, NOT_FOUND_LOCATION, ALIASES, BLACKLIST, IATA_CODES_FILE, IPLOCATION_ORDER, IPINFO_TOKEN, ) GEOIP_READER = geoip2.database.Reader(GEOLITE) COUNTRY_MAP = {"Russian Federation": "Russia"} def _debug_log(s): if os.environ.get("WTTR_DEBUG_LOCATION"): with open("/tmp/location-debug.log", "a") as f: f.write("%s %s\n" % (datetime.datetime.now(), s)) def _is_ip(ip_addr): """ Check if `ip_addr` looks like an IP Address """ if sys.version_info[0] < 3: ip_addr = ip_addr.encode("utf-8") try: socket.inet_pton(socket.AF_INET, ip_addr) return True except socket.error: try: socket.inet_pton(socket.AF_INET6, ip_addr) return True except socket.error: return False def _location_normalize(location): """ Normalize location name `location` """ # translation_table = dict.fromkeys(map(ord, '!@#$*;'), None) def _remove_chars(chars, string): return "".join(x for x in string if x not in chars) location = location.lower().replace("_", " ").replace("+", " ").strip() if not location.startswith("moon@"): location = _remove_chars(r"!@#$*;:\\", location) return location def _geolocator(location): """ Return a GPS pair for specified `location` or None if nothing can be found """ try: if random.random() < 0: geo = requests.get("%s/%s" % (GEOLOCATOR_SERVICE, location)).text else: geo = requests.get( "http://127.0.0.1:8085/:geo-location?location=%s" % location ).text except requests.exceptions.ConnectionError as exception: print("ERROR: %s" % exception) return None if geo == "": return None try: answer = json.loads(geo.encode("utf-8")) if "error" in answer: return None return answer except ValueError as exception: print("ERROR: %s" % exception) return None return None def _ipcachewrite(ip_addr, location): """Write a retrieved ip+location into cache Can stress some filesystems after long term use, see https://stackoverflow.com/questions/466521/how-many-files-can-i-put-in-a-directory Expects a location of the form: `(city, region, country, country_code, , )` Writes a cache entry of the form: `country_code;country;region;city;;` The latitude and longitude are optional elements. """ return cachefile = os.path.join(IP2LCACHE, ip_addr) if not os.path.exists(IP2LCACHE): os.makedirs(IP2LCACHE) with open(cachefile, "w") as file: # like ip2location format file.write( location[3] + ";" + location[2] + ";" + location[1] + ";" + location[0] ) if len(location) > 4: file.write(";" + ";".join(map(str, location[4:]))) def _ipcache(ip_addr): """Retrieve a location from cache by ip addr Returns a triple of (CITY, REGION, COUNTRY) or None TODO: When cache becomes more robust, transition to using latlong """ ## Use Geo IP service when available r = requests.get("http://127.0.0.1:8085/:geo-ip-get?ip=%s" % ip_addr) if r.status_code == 200 and ";" in r.text: _, country, region, city, *_ = r.text.split(";") return city, region, country return None # cachefile = os.path.join(IP2LCACHE, ip_addr) # # if os.path.exists(cachefile): # try: # _, country, region, city, *_ = open(cachefile, 'r').read().split(';') # return city, region, country # except ValueError: # # cache entry is malformed: should be # # [ccode];country;region;city;[lat];[long];... # return None # else: # _debug_log("[_ipcache] %s not found" % ip_addr) # return None def _ip2location(ip_addr): """Convert IP address `ip_addr` to a location name using ip2location. Return list of location data fields: [ccode, country, region, city, rest...] Return `None` if an error occured. """ # if IP2LOCATION_KEY is not set, do not query, # because the query wont be processed anyway if not IP2LOCATION_KEY: return None try: _debug_log("[_ip2location] %s search" % ip_addr) r = requests.get( "http://api.ip2location.com/?ip=%s&key=%s&package=WS3" # WS5 provides latlong % (ip_addr, IP2LOCATION_KEY) ) r.raise_for_status() location = r.text parts = location.split(";") if len(parts) >= 4: # ccode, country, region, city, (rest) _debug_log("[_ip2location] %s found" % ip_addr) return [parts[3], parts[2], parts[1], parts[0]] + parts[4:] return None except requests.exceptions.RequestException: return None def _ipinfo(ip_addr): if not IPINFO_TOKEN: return None try: r = requests.get("https://ipinfo.io/%s/json?token=%s" % (ip_addr, IPINFO_TOKEN)) r.raise_for_status() r_json = r.json() # can't do two unpackings on one line city, region, country, ccode = ( r_json["city"], r_json["region"], "", r_json["country"], ) lat, long = r_json["loc"].split(",") # NOTE: ipinfo only provides ISO codes for countries country = pycountry.countries.get(alpha_2=ccode).name except (requests.exceptions.RequestException, ValueError): # latter is thrown by failure to parse json in reponse return None return [city, region, country, ccode, lat, long] def _geoip(ip_addr): try: _debug_log("[_geoip] %s search" % ip_addr) response = GEOIP_READER.city(ip_addr) # print(response.subdivisions) city, region, country, ccode, lat, long = ( response.city.name, response.subdivisions[0].names["en"], response.country.name, response.country.iso_code, response.location.latitude, response.location.longitude, ) _debug_log("[_geoip] %s found" % ip_addr) except IndexError: # Tuple error try: city, region, country, ccode, lat, long = ( response.city.name, None, response.country.name, response.country.iso_code, response.location.latitude, response.location.longitude, ) except IndexError: return None except (geoip2.errors.AddressNotFoundError): return None return [city, region, country, ccode, lat, long] def _country_name_workaround(country): # workaround for strange bug with the country name # maybe some other countries has this problem too country = COUNTRY_MAP.get(country) or country return country def _get_location(ip_addr): """ Return location triple (CITY, REGION, COUNTRY) for `ip_addr` """ location = _ipcache(ip_addr) if location: return location # location from iplocators have the following order: # (CITY, REGION, COUNTRY, CCODE, LAT, LONG) for method in IPLOCATION_ORDER: if method == "geoip": location = _geoip(ip_addr) elif method == "ip2location": location = _ip2location(ip_addr) elif method == "ipinfo": location = _ipinfo(ip_addr) else: print("ERROR: invalid iplocation method specified: %s" % method) if location is not None: break if location is not None and all(location): _ipcachewrite(ip_addr, location) # cache write used to happen before workaround, preserve that location[2] = _country_name_workaround(location[2]) return location[:3] # city, region, country # ccode is cached but not needed for location # temporary disabled it because of geoip services capcacity # # if city is None and response.location: # coord = "%s, %s" % (response.location.latitude, response.location.longitude) # try: # location = geolocator.reverse(coord, language='en') # city = location.raw.get('address', {}).get('city') # except Exception: # pass # No methods resulted in a location - return default return NOT_FOUND_LOCATION, None, None def _location_canonical_name(location): "Find canonical name for `location`" location = _location_normalize(location) if location.lower() in LOCATION_ALIAS: return LOCATION_ALIAS[location.lower()] return location def _load_aliases(aliases_filename): """ Load aliases from the aliases file """ aliases_db = {} with open(aliases_filename, "r") as f_aliases: for line in f_aliases.readlines(): try: from_, to_ = line.decode("utf-8").split(":", 1) except AttributeError: from_, to_ = line.split(":", 1) aliases_db[_location_normalize(from_)] = _location_normalize(to_) return aliases_db def _load_iata_codes(iata_codes_filename): """ Load IATA codes from the IATA codes file """ with open(iata_codes_filename, "r") as f_iata_codes: result = [] for line in f_iata_codes.readlines(): result.append(line.strip()) return set(result) LOCATION_ALIAS = _load_aliases(ALIASES) LOCATION_BLACK_LIST = [x.strip() for x in open(BLACKLIST, "r").readlines()] IATA_CODES = _load_iata_codes(IATA_CODES_FILE) def is_location_blocked(location): """ Return True if this location is blocked or False if it is allowed """ return location is not None and location.lower() in LOCATION_BLACK_LIST def _get_hemisphere(location): """ Return hemisphere of the location (True = North, False = South). Assume North and return True if location can't be found. """ if all(location): location_string = ", ".join(location) else: return True geolocation = _geolocator(location_string) if geolocation is None: return True return float(geolocation["latitude"]) > 0 def _fully_qualified_location(location, region, country): """Return fully qualified location name with `region` and `country`, as a string. """ # If country is not specified, location stays as is if not country: return location # Canonify/shorten country name if country == "United Kingdom of Great Britain and Northern Ireland": country = "United Kingdom" elif country == "Russian Federation": country = "Russia" elif country == "United States of America": country = "United States" # In United States region is important, because there are a lot of # locations with the same name in different regions. # In the rest of the world, usage of region name may decrease chances # or correct name resolution, so for the moment `region` is used # only for the United States if country == "United States" and region: location += ", %s, %s" % (region, country) else: location += ", %s" % country return location def location_processing(location, ip_addr): """ """ # if location is starting with ~ # or has non ascii symbols # it should be handled like a search term (for geolocator) override_location_name = None full_address = None hide_full_address = False force_show_full_address = location is not None and location.startswith("~") # location ~ means that it should be detected automatically, # and shown in the location line below the report if location == "~": location = None if location and location.lstrip("~ ").startswith("@"): try: if location.lstrip("~ ")[1:] == "": location, region, country = NOT_FOUND_LOCATION, None, None else: location, region, country = _get_location( socket.gethostbyname(location.lstrip("~ ")[1:]) ) location = "~" + location location = _fully_qualified_location(location, region, country) hide_full_address = not force_show_full_address except Exception: location, region, country = NOT_FOUND_LOCATION, None, None query_source_location = _get_location(ip_addr) # For moon queries, hemisphere must be found # True for North, False for South hemisphere = False if location is not None and (location.lower() + "@").startswith("moon@"): hemisphere = _get_hemisphere(query_source_location) country = None if not location or location == "MyLocation": location = ip_addr if _is_ip(location): location, region, country = _get_location(location) # location is just city here # here too if location: location = "~" + location location = _fully_qualified_location(location, region, country) hide_full_address = not force_show_full_address if location and not location.startswith("~"): tmp_location = _location_canonical_name(location) if tmp_location != location: override_location_name = location location = tmp_location # up to this point it is possible that the name # contains some unicode symbols # here we resolve them if location is not None and location != NOT_FOUND_LOCATION: location = "~" + location.lstrip("~ ") if not override_location_name: override_location_name = location.lstrip("~") # if location is not None and location.upper() in IATA_CODES: # location = '~%s' % location if ( location is not None and not location.startswith("~-,") and location.startswith("~") ): geolocation = _geolocator(_location_canonical_name(location[1:])) if geolocation is not None: if not override_location_name: override_location_name = location[1:].replace("+", " ") location = "%s,%s" % (geolocation["latitude"], geolocation["longitude"]) country = None if not hide_full_address: full_address = geolocation["address"] else: full_address = None else: location = NOT_FOUND_LOCATION # location[1:] return ( location, override_location_name, full_address, country, query_source_location, hemisphere, ) def _main_(): """Validate cache entries. Print names of invalid cache entries and move it to the "broken-entries" directory.""" import glob import shutil for filename in glob.glob(os.path.join(IP2LCACHE, "*")): ip_address = os.path.basename(filename) data = _ipcache(ip_address) if data: city, region, country = data if any(x in city for x in "0123456789"): print(city) shutil.move( filename, os.path.join("/wttr.in/cache/ip2l-broken-format", ip_address), ) def _trace_ip(): print(_geoip("108.5.186.108")) print(_get_location("108.5.186.108")) print(location_processing("", "108.5.186.108")) if __name__ == "__main__": _trace_ip() # _main_() # print(_geoip("173.216.90.56")) ================================================ FILE: lib/metno.py ================================================ #!/bin/env python # vim: fileencoding=utf-8 from datetime import datetime, timedelta import json import logging import os import re import sys import timezonefinder from pytz import timezone from constants import WWO_CODE logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) logger = logging.getLogger(__name__) def metno_request(path, query_string): # We'll need to sanitize the inbound request - ideally the # premium/v1/weather.ashx portion would have always been here, though # it seems as though the proxy was built after the majority of the app # and not refactored. For WAPI we'll strip this and the API key out, # then manage it on our own. logger.debug("Original path: " + path) logger.debug("Original query: " + query_string) path = path.replace( "premium/v1/weather.ashx", "weatherapi/locationforecast/2.0/complete" ) query_string = re.sub(r"key=[^&]*&", "", query_string) query_string = re.sub(r"format=[^&]*&", "", query_string) days = int(re.search(r"num_of_days=([0-9]+)&", query_string).group(1)) query_string = re.sub(r"num_of_days=[0-9]+&", "", query_string) # query_string = query_string.replace('key=', '?key=' + WAPI_KEY) # TP is for hourly forecasting, which isn't available in the free api. query_string = re.sub(r"tp=[0-9]*&", "", query_string) # This assumes lang=... is at the end. Also note that the API doesn't # localize, and we're not either. TODO: add language support query_string = re.sub(r"lang=[^&]*$", "", query_string) query_string = re.sub(r"&$", "", query_string) logger.debug("qs: " + query_string) # Deal with coordinates. Need to be rounded to 4 decimals for metno ToC # and in a different query string format coord_match = re.search(r"q=[^&]*", query_string) coords_str = coord_match.group(0) coords = re.findall(r"[-0-9.]+", coords_str) lat = str(round(float(coords[0]), 4)) lng = str(round(float(coords[1]), 4)) logger.debug("lat: " + lat) logger.debug("lng: " + lng) query_string = re.sub(r"q=[^&]*", "lat=" + lat + "&lon=" + lng + "&", query_string) logger.debug("Return path: " + path) logger.debug("Return query: " + query_string) return path, query_string, days def celsius_to_f(celsius): return round((1.8 * celsius) + 32, 1) def to_weather_code(symbol_code): logger.debug(symbol_code) code = re.sub(r"_.*", "", symbol_code) logger.debug(code) # symbol codes: https://api.met.no/weatherapi/weathericon/2.0/documentation # they also have _day, _night and _polartwilight variants # See json from https://api.met.no/weatherapi/weathericon/2.0/legends # WWO codes: https://github.com/chubin/wttr.in/blob/master/lib/constants.py # http://www.worldweatheronline.com/feed/wwoConditionCodes.txt weather_code_map = { "clearsky": 113, "cloudy": 119, "fair": 116, "fog": 143, "heavyrain": 302, "heavyrainandthunder": 389, "heavyrainshowers": 305, "heavyrainshowersandthunder": 386, "heavysleet": 314, # There's a ton of 'LightSleet' in WWO_CODE... "heavysleetandthunder": 377, "heavysleetshowers": 362, "heavysleetshowersandthunder": 374, "heavysnow": 230, "heavysnowandthunder": 392, "heavysnowshowers": 371, "heavysnowshowersandthunder": 392, "lightrain": 266, "lightrainandthunder": 200, "lightrainshowers": 176, "lightrainshowersandthunder": 386, "lightsleet": 281, "lightsleetandthunder": 377, "lightsleetshowers": 284, "lightsnow": 320, "lightsnowandthunder": 392, "lightsnowshowers": 368, "lightssleetshowersandthunder": 365, "lightssnowshowersandthunder": 392, "partlycloudy": 116, "rain": 293, "rainandthunder": 389, "rainshowers": 299, "rainshowersandthunder": 386, "sleet": 185, "sleetandthunder": 392, "sleetshowers": 263, "sleetshowersandthunder": 392, "snow": 329, "snowandthunder": 392, "snowshowers": 230, "snowshowersandthunder": 392, } if code not in weather_code_map: logger.debug("not found") return -1 # not found logger.debug(weather_code_map[code]) return weather_code_map[code] def to_description(symbol_code): desc = WWO_CODE[str(to_weather_code(symbol_code))] logger.debug(desc) return desc def to_16_point(degrees): # 360 degrees / 16 = 22.5 degrees of arc or 11.25 degrees around the point if degrees > (360 - 11.25) or degrees <= 11.25: return "N" if degrees > 11.25 and degrees <= (11.25 + 22.5): return "NNE" if degrees > (11.25 + (22.5 * 1)) and degrees <= (11.25 + (22.5 * 2)): return "NE" if degrees > (11.25 + (22.5 * 2)) and degrees <= (11.25 + (22.5 * 3)): return "ENE" if degrees > (11.25 + (22.5 * 3)) and degrees <= (11.25 + (22.5 * 4)): return "E" if degrees > (11.25 + (22.5 * 4)) and degrees <= (11.25 + (22.5 * 5)): return "ESE" if degrees > (11.25 + (22.5 * 5)) and degrees <= (11.25 + (22.5 * 6)): return "SE" if degrees > (11.25 + (22.5 * 6)) and degrees <= (11.25 + (22.5 * 7)): return "SSE" if degrees > (11.25 + (22.5 * 7)) and degrees <= (11.25 + (22.5 * 8)): return "S" if degrees > (11.25 + (22.5 * 8)) and degrees <= (11.25 + (22.5 * 9)): return "SSW" if degrees > (11.25 + (22.5 * 9)) and degrees <= (11.25 + (22.5 * 10)): return "SW" if degrees > (11.25 + (22.5 * 10)) and degrees <= (11.25 + (22.5 * 11)): return "WSW" if degrees > (11.25 + (22.5 * 11)) and degrees <= (11.25 + (22.5 * 12)): return "W" if degrees > (11.25 + (22.5 * 12)) and degrees <= (11.25 + (22.5 * 13)): return "WNW" if degrees > (11.25 + (22.5 * 13)) and degrees <= (11.25 + (22.5 * 14)): return "NW" if degrees > (11.25 + (22.5 * 14)) and degrees <= (11.25 + (22.5 * 15)): return "NNW" def meters_to_miles(meters): return round(meters * 0.00062137, 2) def mm_to_inches(mm): return round(mm / 25.4, 2) def hpa_to_mb(hpa): return hpa def hpa_to_in(hpa): return round(hpa * 0.02953, 2) def hpa_to_mmHg(hpa): return round(hpa * 0.75006157584566, 3) def group_hours_to_days(lat, lng, hourlies, days_to_return): tf = timezonefinder.TimezoneFinder() timezone_str = tf.certain_timezone_at(lat=lat, lng=lng) logger.debug("got TZ: " + timezone_str) tz = timezone(timezone_str) start_day_gmt = datetime.fromisoformat(hourlies[0]["time"].replace("Z", "+00:00")) start_day_local = start_day_gmt.astimezone(tz) end_day_local = (start_day_local + timedelta(days=days_to_return - 1)).date() logger.debug("series starts at gmt time: " + str(start_day_gmt)) logger.debug("series starts at local time: " + str(start_day_local)) logger.debug("series ends on day: " + str(end_day_local)) days = {} for hour in hourlies: current_day_gmt = datetime.fromisoformat(hour["time"].replace("Z", "+00:00")) current_local = current_day_gmt.astimezone(tz) current_day_local = current_local.date() if current_day_local > end_day_local: continue if current_day_local not in days: days[current_day_local] = {"hourly": []} hour["localtime"] = current_local.time() days[current_day_local]["hourly"].append(hour) # Need a second pass to build the min/max/avg data for date, day in days.items(): minTempC = -999 maxTempC = 1000 avgTempC = None n = 0 maxUvIndex = 0 for hour in day["hourly"]: temp = hour["data"]["instant"]["details"]["air_temperature"] if temp > minTempC: minTempC = temp if temp < maxTempC: maxTempC = temp if avgTempC is None: avgTempC = temp n = 1 else: avgTempC = ((avgTempC * n) + temp) / (n + 1) n = n + 1 uv = hour["data"]["instant"]["details"] if "ultraviolet_index_clear_sky" in uv: if uv["ultraviolet_index_clear_sky"] > maxUvIndex: maxUvIndex = uv["ultraviolet_index_clear_sky"] day["maxtempC"] = str(maxTempC) day["maxtempF"] = str(celsius_to_f(maxTempC)) day["mintempC"] = str(minTempC) day["mintempF"] = str(celsius_to_f(minTempC)) day["avgtempC"] = str(round(avgTempC, 1)) day["avgtempF"] = str(celsius_to_f(avgTempC)) # day["totalSnow_cm": "not implemented", # day["sunHour": "12", # This would come from astonomy data day["uvIndex"] = str(maxUvIndex) return days def _convert_hour(hour): # Whatever is upstream is expecting data in the shape of WWO. This method will # morph from metno to hourly WWO response format. # Note that WWO is providing data every 3 hours. Metno provides every hour # { # "time": "0", # "tempC": "19", # "tempF": "66", # "windspeedMiles": "6", # "windspeedKmph": "9", # "winddirDegree": "276", # "winddir16Point": "W", # "weatherCode": "119", # "weatherIconUrl": [ # { # "value": "http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0003_white_cloud.png" # } # ], # "weatherDesc": [ # { # "value": "Cloudy" # } # ], # "precipMM": "0.0", # "precipInches": "0.0", # "humidity": "62", # "visibility": "10", # "visibilityMiles": "6", # "pressure": "1017", # "pressureInches": "31", # "cloudcover": "66", # "HeatIndexC": "19", # "HeatIndexF": "66", # "DewPointC": "12", # "DewPointF": "53", # "WindChillC": "19", # "WindChillF": "66", # "WindGustMiles": "8", # "WindGustKmph": "13", # "FeelsLikeC": "19", # "FeelsLikeF": "66", # "chanceofrain": "0", # "chanceofremdry": "93", # "chanceofwindy": "0", # "chanceofovercast": "89", # "chanceofsunshine": "18", # "chanceoffrost": "0", # "chanceofhightemp": "0", # "chanceoffog": "0", # "chanceofsnow": "0", # "chanceofthunder": "0", # "uvIndex": "1" details = hour["data"]["instant"]["details"] if "next_1_hours" in hour["data"]: next_hour = hour["data"]["next_1_hours"] elif "next_6_hours" in hour["data"]: next_hour = hour["data"]["next_6_hours"] elif "next_12_hours" in hour["data"]: next_hour = hour["data"]["next_12_hours"] else: next_hour = {} # Need to dig out symbol_code and precipitation_amount symbol_code = "clearsky_day" # Default to sunny if "summary" in next_hour and "symbol_code" in next_hour["summary"]: symbol_code = next_hour["summary"]["symbol_code"] precipitation_amount = 0 # Default to no rain if "details" in next_hour and "precipitation_amount" in next_hour["details"]: precipitation_amount = next_hour["details"]["precipitation_amount"] uvIndex = 0 # default to 0 index if "ultraviolet_index_clear_sky" in details: uvIndex = details["ultraviolet_index_clear_sky"] localtime = "" if "localtime" in hour: localtime = "{h:02.0f}".format(h=hour["localtime"].hour) + "{m:02.0f}".format( m=hour["localtime"].minute ) logger.debug(str(hour["localtime"])) # time property is local time, 4 digit 24 hour, with no :, e.g. 2100 return { "time": localtime, "observation_time": hour["time"], # Need to figure out WWO TZ # temp_C is used in we-lang.go calcs in such a way # as to expect a whole number "temp_C": str(int(round(details["air_temperature"], 0))), # temp_F can be more precise - not used in we-lang.go calcs "temp_F": str(celsius_to_f(details["air_temperature"])), "weatherCode": str(to_weather_code(symbol_code)), "weatherIconUrl": [ { "value": "not yet implemented", } ], "weatherDesc": [ { "value": to_description(symbol_code), } ], # similiarly, windspeedMiles is not used by we-lang.go, but kmph is "windspeedMiles": str(meters_to_miles(details["wind_speed"])), "windspeedKmph": str(int(round(details["wind_speed"], 0))), "winddirDegree": str(details["wind_from_direction"]), "winddir16Point": to_16_point(details["wind_from_direction"]), "precipMM": str(precipitation_amount), "precipInches": str(mm_to_inches(precipitation_amount)), "humidity": str(details["relative_humidity"]), "visibility": "not yet implemented", # str(details['vis_km']), "visibilityMiles": "not yet implemented", # str(details['vis_miles']), "pressure": str(hpa_to_mb(details["air_pressure_at_sea_level"])), "pressure_mmHg": str(hpa_to_mmHg(details["air_pressure_at_sea_level"])), "pressureInches": str(hpa_to_in(details["air_pressure_at_sea_level"])), "cloudcover": "not yet implemented", # Convert from cloud_area_fraction?? str(details['cloud']), # metno doesn't have FeelsLikeC, but we-lang.go is using it in calcs, # so we shall set it to temp_C "FeelsLikeC": str(int(round(details["air_temperature"], 0))), "FeelsLikeF": "not yet implemented", # str(details['feelslike_f']), "uvIndex": str(uvIndex), } def _convert_hourly(hours): converted_hours = [] for hour in hours: converted_hours.append(_convert_hour(hour)) return converted_hours # Whatever is upstream is expecting data in the shape of WWO. This method will # morph from metno to WWO response format. def create_standard_json_from_metno(content, days_to_return): try: forecast = json.loads(content) # pylint: disable=invalid-name except (ValueError, TypeError) as exception: logger.error("---") logger.error(exception) logger.error("---") return {}, "" hourlies = forecast["properties"]["timeseries"] current = hourlies[0] # We are assuming these units: # "units": { # "air_pressure_at_sea_level": "hPa", # "air_temperature": "celsius", # "air_temperature_max": "celsius", # "air_temperature_min": "celsius", # "cloud_area_fraction": "%", # "cloud_area_fraction_high": "%", # "cloud_area_fraction_low": "%", # "cloud_area_fraction_medium": "%", # "dew_point_temperature": "celsius", # "fog_area_fraction": "%", # "precipitation_amount": "mm", # "relative_humidity": "%", # "ultraviolet_index_clear_sky": "1", # "wind_from_direction": "degrees", # "wind_speed": "m/s" # } content = { "data": { "request": [ { "type": "feature", "query": str(forecast["geometry"]["coordinates"][1]) + "," + str(forecast["geometry"]["coordinates"][0]), } ], "current_condition": [_convert_hour(current)], "weather": [], } } days = group_hours_to_days( forecast["geometry"]["coordinates"][1], forecast["geometry"]["coordinates"][0], hourlies, days_to_return, ) # TODO: Astronomy needs to come from this: # https://api.met.no/weatherapi/sunrise/2.0/.json?lat=40.7127&lon=-74.0059&date=2020-10-07&offset=-05:00 # and obviously can be cached for a while # https://api.met.no/weatherapi/sunrise/2.0/documentation # Note that full moon/new moon/first quarter/last quarter aren't returned # and the moonphase value should match these from WWO: # New Moon # Waxing Crescent # First Quarter # Waxing Gibbous # Full Moon # Waning Gibbous # Last Quarter # Waning Crescent for date, day in days.items(): content["data"]["weather"].append( { "date": str(date), "astronomy": [], "maxtempC": day["maxtempC"], "maxtempF": day["maxtempF"], "mintempC": day["mintempC"], "mintempF": day["mintempF"], "avgtempC": day["avgtempC"], "avgtempF": day["avgtempF"], "totalSnow_cm": "not implemented", "sunHour": "12", # This would come from astonomy data "uvIndex": day["uvIndex"], "hourly": _convert_hourly(day["hourly"]), } ) # for day in forecast. return json.dumps(content) if __name__ == "__main__": # if len(sys.argv) == 1: # for deg in range(0, 360): # print('deg: ' + str(deg) + '; 16point: ' + to_16_point(deg)) if len(sys.argv) == 2: req = sys.argv[1].split("?") # to_description(sys.argv[1]) metno_request(req[0], req[1]) elif len(sys.argv) == 3: with open(sys.argv[1], "r") as contentf: content = create_standard_json_from_metno(contentf.read(), int(sys.argv[2])) print(content) else: print("usage: metno ") ================================================ FILE: lib/parse_query.py ================================================ import re import json import zlib import base64 def serialize(parsed_query): return base64.b64encode( zlib.compress(json.dumps(parsed_query).encode("utf-8")), altchars=b"-_" ).decode("utf-8") def deserialize(url): string = url[2:] extension = None if "." in string: string, extension = string.split(".", 1) try: result = json.loads( zlib.decompress(base64.b64decode(string, altchars=b"-_")).decode("utf-8") ) except zlib.error: return None if extension == "png": result["png_filename"] = url result["html_output"] = False return result def metric_or_imperial(query, lang, us_ip=False): """ """ # what units should be used # metric or imperial # based on query and location source (imperial for US by default) if query.get("use_metric", False) and not query.get("use_imperial", False): query["use_imperial"] = False query["use_metric"] = True elif query.get("use_imperial", False) and not query.get("use_metric", False): query["use_imperial"] = True query["use_metric"] = False elif lang == "us": # slack uses m by default, to override it speciy us.wttr.in query["use_imperial"] = True query["use_metric"] = False else: if us_ip: query["use_imperial"] = True query["use_metric"] = False else: query["use_imperial"] = False query["use_metric"] = True return query def parse_query(args): result = {} reserved_args = ["lang"] q = "" for key, val in args.items(): if len(val) == 0: q += key continue if val == "True": val = True if val == "False": val = False result[key] = val if q is None: return result if "A" in q: result["force-ansi"] = True if "d" in q: result["dumb"] = True if "n" in q: result["narrow"] = True if "m" in q: result["use_metric"] = True if "M" in q: result["use_metric"] = True result["use_ms_for_wind"] = True if "u" in q: result["use_imperial"] = True if "I" in q: result["inverted_colors"] = True if "t" in q: result["transparency"] = "150" if "T" in q: result["no-terminal"] = True if "p" in q: result["padding"] = True for days in "0123": if days in q: result["days"] = days if "q" in q: result["no-caption"] = True if "Q" in q: result["no-city"] = True if "F" in q: result["no-follow-line"] = True for key, val in args.items(): if val == "True": val = True if val == "False": val = False if val: result[key] = val # currently `view` is alias for `format` if "format" in result and not result.get("view"): result["view"] = result["format"] del result["format"] return result def parse_wttrin_png_name(name): """ Parse the PNG filename and return the result as a dictionary. For example: input = City_200x_lang=ru.png output = { "lang": "ru", "width": "200", "filetype": "png", "location": "City" } """ parsed = {} to_be_parsed = {} if name.lower()[-4:] == ".png": parsed["filetype"] = "png" name = name[:-4] parts = name.split("_") parsed["location"] = parts[0] one_letter_options = "" for part in parts[1:]: if re.match("(?:[0-9]+)x", part): parsed["width"] = part[:-1] elif re.match("x(?:[0-9]+)", part): parsed["height"] = part[1:] elif re.match(part, "(?:[0-9]+)x(?:[0-9]+)"): parsed["width"], parsed["height"] = part.split("x", 1) elif "=" in part: arg, val = part.split("=", 1) to_be_parsed[arg] = val else: one_letter_options += part for letter in one_letter_options: to_be_parsed[letter] = "" parsed.update(parse_query(to_be_parsed)) # currently `view` is alias for `format` if "format" in parsed and not parsed.get("view"): parsed["view"] = parsed["format"] del parsed["format"] return parsed ================================================ FILE: lib/proxy_log.py ================================================ """ Logger of proxy queries """ # pylint: disable=consider-using-with,too-few-public-methods import datetime class Logger: """ Generic logger. For specific loggers, _shorten_query() should be rewritten. """ def __init__(self, filename_access, filename_errors): self._filename_access = filename_access self._filename_errors = filename_errors self._log_access = open(filename_access, "a", encoding="utf-8") self._log_errors = open(filename_errors, "a", encoding="utf-8") def _shorten_query(self, query): return query def log(self, query, error): """ Log `query` and `error` """ message = str(datetime.datetime.now()) query = self._shorten_query(query) if error != "": message += " ERR " + query + " " + error self._log_errors.write(message + "\n") self._log_errors.flush() else: message += " OK " + query self._log_access.write(message + "\n") self._log_access.flush() class LoggerWWO(Logger): """ WWO logger. """ def _shorten_query(self, query): return "".join([x for x in query.split("&") if x.startswith("q=")]) ================================================ FILE: lib/translations.py ================================================ # vim: fileencoding=utf-8 """ Translation of almost everything. """ FULL_TRANSLATION = [ "am", "ar", "af", "be", "bn", "ca", "da", "de", "el", "et", "fr", "fa", "gl", "hi", "hu", "ia", "id", "it", "lt", "mg", "nb", "nl", "oc", "pl", "pt-br", "ro", "ru", "ta", "tr", "th", "uk", "vi", "zh-cn", "zh-tw", ] PARTIAL_TRANSLATION = [ "az", "bg", "bs", "cy", "cs", "eo", "es", "eu", "fi", "ga", "hi", "hr", "hy", "is", "ja", "jv", "ka", "kk", "ko", "ky", "lv", "mk", "ml", "mr", "nl", "fy", "nn", "pt", "pt-br", "sk", "sl", "sr", "sr-lat", "sv", "sw", "te", "uz", "zh", "zu", "he", ] PROXY_LANGS = [ "af", "am", "ar", "az", "be", "bn", "bs", "ca", "cy", "de", "el", "eo", "et", "eu", "fa", "fr", "fy", "ga", "gl", "he", "hr", "hu", "hy", "ia", "id", "is", "it", "ja", "kk", "lt", "lv", "mg", "mk", "mr", "nb", "nn", "oc", "ro", "ru", "sl", "th", "pt-br", "uk", "uz", "vi", "zh-cn", "zh-tw", ] SUPPORTED_LANGS = FULL_TRANSLATION + PARTIAL_TRANSLATION MESSAGE = { "NOT_FOUND_MESSAGE": { "en": """ We were unable to find your location so we have brought you to Oymyakon, one of the coldest permanently inhabited locales on the planet. """, "af": """ Ons kon nie u ligging opspoor nie gevolglik het ons vir u na Oymyakon geneem, een van die koudste permanent bewoonde plekke op aarde. """, "am": """ አካባቢዎን ማግኘት ስላልቻልን ወደ ኦይማያኮን አምጥተንዎታል፤ በአለም ላይ በጣም ቀዝቃዛ እና ሰው አልባ ከሆኑት ቦታዎች አንዱ። """, "ar": """ تعذر علينا العثور على موقعك لذلك قمنا بجلبك إلي أويمياكون, إحدى الأماكن المَأْهُولة الأكثر برودة علي الإطلاق في هذا الكوكب. """, "be": """ Ваша месцазнаходжанне вызначыць не атрымалася, таму мы пакажам вам надвор'е ў Аймяконе, самым халодным населеным пункце на планеце. Будзем спадзявацца, што ў вас сёння надвор'е лепей! """, "bg": """ Не успяхме да открием вашето местоположение така че ви доведохме в Оймякон, едно от най-студените постоянно обитавани места на планетата. """, "bn": """ দুঃখিত, আপনার অবস্থান আমরা খুঁজে পাইনি। তাই, আমরা আপনাকে নিয়ে এসেছি ওয়মিয়াকনে, যা পৃথিবীর শীতলতম স্থায়ী জন-বসতিগুলোর একটি। """, "bs": """ Nismo mogli pronaći vašu lokaciju, tako da smo te doveli do Oymyakon, jedan od najhladnijih stalno naseljena mjesta na planeti. Nadamo se da ćete imati bolje vreme! """, "ca": """ Hem estat incapaços de trobar la seva ubicació, per això l'hem portat fins Oymyakon, un dels llocs més freds i permanentment deshabitats del planeta. """, "cs": """ Nepodařilo se nám najít vaši polohu, takže jsme vás přivedli do Ojmjakonu. Je to jedno z nejchladnějších trvale obydlených míst na planetě. Doufáme, že budete mít lepší počasí! """, "cy": """ Ni darganfyddwyd eich lleoliad, felly rydym wedi dod â chi i Oymyakon, un o'r llefydd oeraf ar y blaned ble mae pobl yn dal i fyw! """, "de": """ Wir konnten Ihren Standort nicht finden, also haben wir Sie nach Oimjakon gebracht, einer der kältesten dauerhaft bewohnten Orte auf dem Planeten. Wir hoffen, dass Sie besseres Wetter haben! """, "el": """ Δεν μπορέσαμε να βρούμε την τοποθεσία σου, για αυτό διαλέξαμε το Οϊμιάκον για εσένα, μία από τις πιο κρύες μόνιμα κατοικημένες περιοχές στον πλανήτη. Ελπίζουμε να έχεις καλύτερο καιρό! """, "es": """ No hemos logrado encontrar tu ubicación, así que hemos decidido enseñarte el tiempo en Oimiakón, uno de los sitios más fríos y permanentemente deshabitados del planeta. """, "eu": """ Ezin izan dugu zure kokapena aurkitu, beraz eguraldia Oymyakonen erakustea erabaki dugu, munduko lekurik hotz eta hutseneraiko bat """, "fa": """ ما نتونستیم مکان شما رو پیدا کنیم. به همین خاطر شما رو به اویمیاکن بردیم ، یکی از سردترین مکان های روی زمین که اصلا قابل سکونت نیست! """, "fi": """ Emme löytänyt sijaintiasi, joten toimme sinut Oimjakoniin, yhteen maailman kylmimmistä pysyvästi asutetuista paikoista. Toivottavasti sinulla on parempi sää! """, "fr": """ Nous n'avons pas pu déterminer votre position, Nous vous avons donc amenés à Oïmiakon, l'un des endroits les plus froids habités en permanence sur la planète. Nous espérons qu'il fait meilleur chez vous ! """, "ga": """ Ní rabhamar ábalta do cheantar a aimsiú mar sin thugamar go dtí Oymyakon, tú ceann do na ceantair bhuanáitrithe is fuaire ar domhan. """, "gl": """ Non logramos atopar a túa localización polo que te trouxemos até Oimiakón, un dos lugares máis fríos e permamentemente deshabitados do planeta. """, "hi": """ हम आपका स्थान खोजने में असमर्थ है, इसलिए हम आपको ओयमयाकोन पर ले आए है, जो ग्रह के सबसे ठंडे स्थानों में से एक है| """, "hu": """ Nem sikerült megtalálni a pozíciódat, így elhoztunk Ojmjakonba; az egyik leghidegebb állandóan lakott településre a bolygón. """, "hy": """ Ձեր գտնվելու վայրը չհաջողվեց որոշել, այդ պատճառով մենք ձեզ կցուցադրենք եղանակը Օյմյակոնում. երկրագնդի ամենասառը բնակավայրում։ Հույս ունենք որ ձեր եղանակը այսօր ավելի լավն է։ """, "ia": """ Nos non trovate su location, assi nos su apporte a Oymyakon, un del plus frigide locos habita super le planeta! """, "id": """ Kami tidak dapat menemukan lokasi anda, jadi kami membawa anda ke Oymyakon, salah satu tempat terdingin yang selalu dihuni di planet ini! """, "is": """ Við finnum ekki staðsetninguna þína og vísum þér þar með á Ojmjakon, ein af köldustu byggðum jarðar. Vonandi er betra veður hjá þér. """, "it": """ Non siamo riusciti a trovare la sua posizione quindi la abbiamo portato a Oymyakon, uno dei luoghi abitualmente abitati più freddi del pianeta. Ci auguriamo che le condizioni dove lei si trova siano migliori! """, "ja": """ 指定された場所が見つかりませんでした。 代わりにオイミャコンの天気予報を表示しています。 オイミャコンは地球上で最も寒い居住地の一つです。 """, "ko": """ 지정된 장소를 찾을 수 없습니다, 대신 오이먀콘의 일기 예보를 표시합니다, 오이먀콘은 지구상에서 가장 추운 곳에 위치한 마을입니다! """, "lt": """ Mums nepavyko rasti jūsų vietovės, todėl mes nukreipėme jus į Omjakoną, vieną iš šalčiausių nuolatinių gyvenviečių planetoje. """, "lv": """ Mēs nevarējām noteikt Tavu atrašanās vietu, tādēļ nogādājām Tevi Oimjakonā, vienā no planētas aukstākajām pastāvīgi apdzīvotajām vietām. """, "mk": """ Неможевме да ја пронајдеме вашата локација, затоа ве однесовме во Ојмајкон, еден од најладните трајно населени места на планетата. """, "mr": """ आमहाला तुमचे स्थळ सापडले नाही. म्हणून आम्ही तुम्हाला ओयम्याकोन येथे आणले आहे, जे कि आपल्या ग्रहावरील सर्वात थंड वस्तिस्थानांपैकी एक आहे. """, "nb": """ Vi kunne ikke finne din lokasjon, så her får du Ojmjakon, et av de kaldeste bebodde stedene på planeten. Vi håper været er bedre hos deg! """, "nl": """ Wij konden uw locatie niet vaststellen dus hebben we u naar Ojmjakon gebracht, één van de koudste permanent bewoonde gebieden op deze planeet. """, "fy": """ Wy koenen jo lokaasje net fêststelle dus wy ha jo nei Ojmjakon brocht, ien fan de kâldste permanent bewenbere plakken op ierde. """, "oc": """ Avèm pas pogut determinar vòstra posicion, Vos avèm doncas menat a Oïmiakon, un dels endreches mai freds abitat permanéncia del monde. Esperam que fa melhor en çò vòstre ! """, "pt": """ Não conseguimos encontrar a sua localização, então decidimos te mostrar o tempo em Oymyakon, um dos lugares mais frios e permanentemente desabitados do planeta. """, "pt-br": """ Não conseguimos encontrar a sua localização, então decidimos te mostrar o tempo em Oymyakon, um dos lugares mais frios e permanentemente desabitados do planeta. """, "pl": """ Nie udało nam się znaleźć podanej przez Ciebie lokalizacji, więc zabraliśmy Cię do Ojmiakonu, jednego z najzimniejszych, stale zamieszkanych miejsc na Ziemi. Mamy nadzieję, że u Ciebie jest cieplej! """, "ro": """ Nu v-am putut identifica localitatea, prin urmare vă arătăm vremea din Oimiakon, una dintre cele mai reci localități permanent locuite de pe planetă. Sperăm că aveți vreme mai bună! """, "ru": """ Ваше местоположение определить не удалось, поэтому мы покажем вам погоду в Оймяконе, самом холодном населённом пункте на планете. Будем надеяться, что у вас сегодня погода лучше! """, "sk": """ Nepodarilo sa nám nájsť vašu polohu, takže sme vás priviedli do Ojmiakonu. Je to jedno z najchladnejších trvale obývaných miest na planéte. Dúfame, že budete mať lepšie počasie! """, "sr": """ Нисмо успели да пронађемо Вашу локацију, па смо Вас довели у Ојмјакон, једно од најхладнијих стално насељених места на планети. Надамо се да је време код Вас боље него што је то случај овде! """, "sv": """ Vi lyckades inte hitta er plats så vi har istället tagit er till Ojmjakon, en av planetens kallaste platser med permanent bosättning. Vi hoppas att vädret är bättre hos dig! """, "tr": """ Aradığınız konum bulunamadı. O yüzden sizi dünyadaki en soğuk sürekli yerleşim yerlerinden biri olan Oymyakon'e getirdik. Umarız sizin olduğunuz yerde havalar daha iyidir! """, "te": """ మేము మీ స్థానాన్ని కనుగొనలేకపోయాము కనుక మనం "ఓమాయకాన్కు" తీసుకొని వచ్చాము, భూమిపై అత్యల్ప శాశ్వతంగా నివసించే స్థానిక ప్రదేశాలలో ఒకటి. """, "th": """ เราไม่สามารถหาตำแหน่งของคุณได้เราจึงนำคุณไปสู่ Oymyakon หมู่บ้านที่หนาวที่สุดในโลก! """, "uk": """ Ми не змогли визначити Ваше місцезнаходження, тому покажемо Вам погоду в Оймяконі — найхолоднішому населеному пункті на планеті. Будемо сподіватися, що у Вас сьогодні погода краще! """, "uz": """ Sizning joylashuvingizni aniqlay olmadik, shuning uchun sizga sayyoramizning eng sovuq aholi punkti - Oymyakondagi ob-havo haqida ma'lumot beramiz. Umid qilamizki, sizda bugungi ob-havo bundan yaxshiroq! """, "zh": """ 我们无法找到您的位置, 当前显示奥伊米亚康(Oymyakon),这个星球上最冷的人类定居点。 """, "da": """ Vi kunne desværre ikke finde din lokation så vi har bragt dig til Oymyakon, En af koldeste og helt ubolige lokationer på planeten. """, "et": """ Me ei suutnud tuvastada teie asukohta ning seetõttu paigutasime teid Oymyakoni, mis on üks kõige külmemaid püsivalt asustatud paiku planeedil. """, "vi": """ Chúng tôi không tìm thấy địa điểm của bạn vì vậy chúng tôi đưa bạn đến Oymyakon, một trong những nơi lạnh nhất có người sinh sống trên trái đất. """, "zh-tw": """ 我們找不到您的位置 所以我們帶您到奧伊米亞康, 這個星球上有人類定居最冷之處。 """, "mg": """ Tsy hita ny toerana misy anao koa nentinay tany Oymyakon ianao, iray amin'ireo toerana mangatsiaka indrindra tsisy mponina eto an-tany. """, "ta": """ உங்கள் இருப்பிடத்தை எங்களால் கண்டுபிடிக்க முடியவில்லை எனவே நாங்கள் உங்களை ஓமியாகோனுக்கு அழைத்து வந்தோம். கிரகத்தின் குளிர்ந்த நிரந்தரமாக வசிக்கும் இடங்களில் ஒன்று. """, }, "UNKNOWN_LOCATION": { "en": "Unknown location", "af": "Onbekende ligging", "am": "ያልታወቀ ቦታ", "ar": "موقع غير معروف", "be": "Невядомае месцазнаходжанне", "bg": "Неизвестно местоположение", "bn": "অজানা অবস্থান", "bs": "Nepoznatoja lokacija", "ca": "Ubicació desconeguda", "cs": "Neznámá poloha", "cy": "Lleoliad anhysbys", "de": "Unbekannter Ort", "da": "Ukendt lokation", "el": "Άνγωστη τοποθεσία", "es": "Ubicación desconocida", "et": "Tundmatu asukoht", "eu": "Kokapen ezezaguna", "fa": "مکان نامعلوم", "fi": "Tuntematon sijainti", "fr": "Emplacement inconnu", "ga": "Ceantar anaithnid", "gl": "Localización descoñecida", "hi": "अज्ञात स्थान", "hu": "Ismeretlen lokáció", "hy": "Անհայտ գտնվելու վայր", "id": "Lokasi tidak diketahui", "ia": "Location incognite", "is": "Óþekkt staðsetning", "it": "Località sconosciuta", "ja": "未知の場所です", "ko": "알 수 없는 장소", "kk": "", "lt": "Nežinoma vietovė", "lv": "Nezināma atrašanās vieta", "mk": "Непозната локација", "mr": "अज्ञात स्थळ", "nb": "Ukjent sted", "nl": "Onbekende locatie", "oc": "Emplaçament desconegut", "fy": "Ûnbekende lokaasje", "pl": "Nieznana lokalizacja", "pt": "Localização desconhecida", "pt-br": "Localização desconhecida", "ro": "Localitate necunoscută", "ru": "Неизвестное местоположение", "sk": "Neznáma poloha", "sl": "Neznano lokacijo", "sr": "Непозната локација", "sv": "Okänd plats", "te": "తెలియని ప్రదేశం", "tr": "Bilinmeyen konum", "th": "ไม่สามารถระบุตำแหน่งได้", "uk": "Невідоме місце", "uz": "Аникланмаган худуд", "zh": "未知地点", "vi": "Địa điểm không xác định", "zh-tw": "未知位置", "mg": "Toerana tsy fantatra", "ta": "தெரியாத இடம்", }, "LOCATION": { "en": "Location", "af": "Ligging", "ar": "الموقع", "be": "Месцазнаходжанне", "bg": "Местоположение", "bn": "অবস্থান", "bs": "Lokacija", "ca": "Ubicació", "cs": "Poloha", "cy": "Lleoliad", "de": "Ort", "da": "Lokation", "el": "Τοποθεσία", "es": "Ubicación", "et": "Asukoht", "eu": "Kokaena", "fa": "مکان", "fi": "Tuntematon sijainti", "fr": "Emplacement", "ga": "Ceantar", "gl": "Localización", "hi": "स्थान", "hu": "Lokáció", "hy": "Դիրք", "ia": "Location", "id": "Lokasi", "is": "Staðsetning", "it": "Località", "ja": "位置情報", "ko": "위치", "kk": "", "lt": "Vietovė", "lv": "Atrašanās vieta", "mk": "Локација", "mr": "स्थळ", "nb": "Sted", "nl": "Locatie", "oc": "Emplaçament", "fy": "Lokaasje", "pl": "Lokalizacja", "pt": "Localização", "pt-br": "Localização", "ro": "Localitate", "ru": "Местоположение", "sk": "Poloha", "sl": "Lokacijo", "sr": "Локација", "sv": "Plats", "zh": "地点", "te": "స్థానము", "tr": "Konum", "th": "ตำแหน่ง", "uk": "Місцезнаходження", "vi": "Địa điểm", "zh-tw": "位置", "mg": "Toerana", "ta": "இடம்", }, "CAPACITY_LIMIT_REACHED": { "en": """ Sorry, we are running out of queries to the weather service at the moment. Here is the weather report for the default city (just to show you what it looks like). We will get new queries as soon as possible. You can follow https://x.com/igor_chubin for the updates. ====================================================================================== """, "af": """ Verskoning, ons oorskry tans die vermoë om navrae aan die weerdiens te rig. Hier is die weerberig van 'n voorbeeld ligging (bloot om aan u te wys hoe dit lyk). Ons sal weereens nuwe navrae kan hanteer so gou as moontlik. U kan vir https://x.com/igor_chubin volg vir opdaterings. ====================================================================================== """, "am": """ ይቅርታ ፤ ለጊዜው መጠይቆችን ማስተናገድ አልቻልንም፡፡ የነባሪ ከተማው የአየር ሁኔታ ሪፖርት ይኸውልዎ (ምን እንደሚመስል ለማሳየት)። አዳዲስ መጠይቆችን በተቻለ ፍጥነት ለማስተናገድ እንሞክራለን። ለበለጠ መረጃ እባክዎ https://x.com/igor_chubin ን ይከተሉ። ====================================================================================== """, "ar": """ عذرًا ، استعلامات خدمة الطقس نفذت في الوقت الحالي. هذا هو تقرير الطقس للمدينة الافتراضية (فقط لتظهر لك كيف تبدو). سوف نتلقى استعلامات جديدة في أقرب وقت ممكن. يمكنك متابعة https://x.com/igor_chubin لآخر المستجدات. ====================================================================================== """, "be": """ Прабачце, мы выйшлі за ліміты колькасці запытаў да службы надвор'я ў дадзены момант. Вось прагноз надвор'я для горада па змаўчанні (толькі, каб паказаць вам, як гэта выглядае). Мы вернемся як мага хутчэй. Вы можаце сачыць на https://x.com/igor_chubin за абнаўленнямі. ====================================================================================== """, "bg": """ Съжаляваме, количеството заявки към услугата за предсказване на време е почти изчерпано. Ето доклад за града по подразбиране (просто да видите как изглежда). Ще осогурим допълнителни заявки максимално бързо. Може да последвате https://x.com/igor_chubin за обновления. """, "bn": """ দুঃখিত, এই মুহুর্তে আবহাওয়া পরিসেবাতে আমাদের কুইরী শেষ হয়ে আসছে। এখানে ডিফল্ট শহরের আবহাওয়ার প্রতিবেদন রয়েছে (এটি দেখতে কেমন তা আপনাকে দেখানোর জন্য)। আমরা খুব দ্রুত নতুন কুইরী পাওয়ার ব্যবস্থা করছি। আপডেটের জন্য আপনি https://x.com/igor_chubin অনুসরণ করতে পারেন। """, "bs": """ Žao mi je, mi ponestaje upita i vremenska prognoza u ovom trenutku. Ovdje je izvještaj o vremenu za default grada (samo da vam pokažem kako to izgleda). Mi ćemo dobiti nove upite u najkraćem mogućem roku. Možete pratiti https://x.com/igor_chubin za ažuriranja. ====================================================================================== """, "ca": """ Disculpa'ns, ens hem quedat momentàniament sense consultes al servei meteorològic. Aquí t'oferim l'informe del temps a la ciutat per defecte (només per mostrar quin aspecte té). Obtindrem noves consultes tan aviat com ens sigui possible. Pots seguir https://x.com/igor_chubin per noves actualitzacions. ====================================================================================== """, "cs": """ Omlouváme se, ale aktuálně nám dochází počet dotazů na službu počasí. Zde je předpověď počasí pro výchozí město (abyste viděli, jak to vypadá). Problém bude vyřešen co nejdříve. Aktualizace můžete následovat na https://x.com/igor_chubin. ====================================================================================== """, "de": """ Entschuldigung, wir können momentan den Wetterdienst nicht erreichen. Dafür zeigen wir Ihnen das Wetter an einem Beispielort, damit Sie sehen, wie die Seite das Wetter anzeigt. Wir werden versuchen das Problem so schnell wie möglich zu beheben. Folgen Sie https://x.com/igor_chubin für Updates. ====================================================================================== """, "cy": """ Rydym yn brin o ymholiadau i'r gwasanaeth tywydd ar hyn o bryd. Felly dyma'r adroddiad tywydd ar gyfer y ddinas ragosod (er mwyn arddangos sut mae'n edrych). Byddwn gyda ymholiadau newydd yn fuan. Gellir dilyn https://x.com/igor_chubin i gael newyddion pellach. ====================================================================================== """, "es": """ Lo siento, hemos alcanzado el límite de peticiones al servicio de previsión del tiempo en este momento. A continuación, la previsión del tiempo para una ciudad estándar (solo para que puedas ver el formato del reporte). Muy pronto volveremos a tener acceso a las peticiones. Puedes seguir https://x.com/igor_chubin para estar al tanto de la situación. ====================================================================================== """, "fa": """ متأسفانه در حال حاضر ظرفیت ما برای درخواست به سرویس هواشناسی به اتمام رسیده. اینجا می تونید گزارش هواشناسی برای شهر پیش فرض رو ببینید (فقط برای اینه که بهتون نشون بدیم چه شکلی هست) ما تلاش میکنیم در اسرع وقت ظرفیت جدید به دست بیاریم. برای دنبال کردن اخبار جدید میتونید https://x.com/igor_chubin رو فالو کنید. ====================================================================================== """, "fr": """ Désolé, nous avons épuisé les requêtes vers le service météo. Voici un bulletin météo de l'emplacement par défaut (pour vous donner un aperçu). Nous serons très bientôt en mesure de faire de nouvelles requêtes. Vous pouvez suivre https://x.com/igor_chubin pour rester informé. ====================================================================================== """, "ga": """ Tá brón orainn, níl mórán iarratas le fail chuig seirbhís na haimsire faoi láthair. Seo duit réamhaisnéis na haimsire don chathair réamhshocraithe (chun é a thaispeaint duit). Gheobhaimid iarratais nua chomh luath agus is feidir. Lean orainn ar https://x.com/igor_chubin don eolas is déanaí. ====================================================================================== """, "gl": """ Desculpa, estamos a chegar ao límite de peticións ao servizo meteorolóxico neste momento. Aquí está a previsión do tempo para a cidade por defecto (tan só para amosarche un exemplo). Imos obter máis peticións tan pronto como poidamos. Podes seguir https://x.com/igor_chubin para estares actualizada. ====================================================================================== """, "hi": """ क्षमा करें, इस समय हम मौसम सेवा से संपर्क नहीं कर पा रहे है। यहा पूर्व निर्धारित शहर के लिए मौसम की जानकारी (आपको यह दिखाने के लिए कि यह कैसा दिखता है)। हम जल्द से जल्द मौसम सेवा से संपर्क करने नई कोशिश करेंगे। आप अपडेट के लिए https://x.com/igor_chubin का अनुसरण कर सकते हैं। ====================================================================================== """, "hu": """ Sajnáljuk, kifogytunk az időjárási szolgáltatásra fordított erőforrásokból. Itt van az alapértelmezett város időjárási jelentése (hogy lásd, hogyan néz ki). A lehető leghamarabb új erőforrásokat fogunk kapni. A frissítésekért tekintsd meg a https://x.com/igor_chubin oldalt. ====================================================================================== """, "hy": """ Կներեք, այս պահին մենք գերազանցել ենք եղանակային տեսության կայանին հարցումների քանակը. Կարող եք տեսնել տիպային եղանակը զեկուցում հիմնական քաղաքի համար (Ուղղակի որպես նմուշ): Մենք մշտապես աշխատում ենք հարցումների քանակը բարելավելու ուղղությամբ: Կարող եք հետևել մեզ https://x.com/igor_chubin թարմացումների համար. ====================================================================================== """, "ia": """ Pardono, nos ha exhaurite inquisitione del servicio tempore alora. Ecce es le reporto tempore por qualque citate (demonstrar le reporte). Nos recipera inquisitione nove tosto. Tu pote abonar a https://x.com/igor_chubin por nove information. ====================================================================================== """, "id": """ Maaf, kami kehabian permintaan ke layanan cuaca saat ini. Ini adalah laporan cuaca dari kota standar (hanya untuk menunjukkan kepada anda bagaimana tampilannya). Kami akan mencoba permintaan baru lagi sesegera mungkin. Anda dapat mengikuti https://x.com/igor_chubin untuk informasi terbaru. ====================================================================================== """, "it": """ Scusate, attualmente stiamo esaurendo le risorse a disposizione del servizio meteo. Qui trovate il bollettino del tempo per la città di default (solo per mostrarvi come si presenta). Potremo elaborare nuove richieste appena possibile. Potete seguire https://x.com/igor_chubin per gli aggiornamenti. ====================================================================================== """, "ko": """ 죄송합니다. 현재 날씨 정보를 가져오는 쿼리 요청이 한도에 도달했습니다. 대신 기본으로 설정된 도시에 대한 일기 예보를 보여드리겠습니다. (이는 단지 어떻게 보이는지 알려주기 위함입니다). 쿼리 요청이 가능한 한 빨리 이루어질 수 있도록 하겠습니다. 업데이트 소식을 원하신다면 https://x.com/igor_chubin 을 팔로우 해주세요. ====================================================================================== """, "lt": """ Atsiprašome, šiuo metu pasiekėme orų prognozės paslaugos užklausų ribą. Štai orų prognozė numatomam miestui (tam, kad parodytume, kaip ji atrodo). Naujas užklausas priimsime, kai tik galėsime. Atnaujinimus galite sekti https://x.com/igor_chubin ====================================================================================== """, "lv": """ Atvainojamies, šobrīd laikapstākļu pakalpojums ir noslogots! Šeit ir laika ziņas noklusējuma pilsētai (lai parādītu, kā tās izskatās). Mēs atsāksim darbu, cik ātri vien varēsim. Var sekot https://x.com/igor_chubin, lai uzzinātu jaunumus. ====================================================================================== """, "mk": """ Извинете, ни снемуваат барања за до сервисот кој ни нуди временска прогноза во моментот. Еве една временска прогноза за град (за да видите како изгледа). Ќе добиеме нови барања најбрзо што можеме. Следете го https://x.com/igor_chubin за известувања ====================================================================================== """, "mr": """ क्षमस्व, याक्षणी आम्ही हवामान सेवेशी संपर्क करू शकत नाही. हा एका पूर्वनिर्धारित शहराचा हवामान अहवाल आहे (केवळ तो कसा दिसतो हे दाखवण्याकरिता). आम्ही लवकरात लवकर सेवा पुनः चालू करण्याचा प्रयत्न करू. अद्यावत माहितीसाठी तुम्ही https://x.com/igor_chubin चे अनुसरण करू शकता. ====================================================================================== """, "nb": """ Beklager, vi kan ikke nå værtjenesten for øyeblikket. Her er værmeldingen for standardbyen så du får se hvordan tjenesten ser ut. Vi vil forsøke å fikse problemet så snart som mulig. Du kan følge https://x.com/igor_chubin for oppdateringer. ====================================================================================== """, "zh": """ 抱歉,当前天气服务不可用。 以下显示默认城市(只对您可见)。 我们将会尽快获取新数据。 您可以通过 https://x.com/igor_chubin 获取最新动态。 ====================================================================================== """, "nl": """ Excuse, wij kunnen u op dit moment dit weerbericht niet laten zien. Hier is het weerbericht voor de standaard stad(zodat u weet hoe het er uitziet) Wij lossen dit probleem zo snel mogelijk op. voor updates kunt u ons op https://x.com/igor_chubin volgen. ====================================================================================== """, "oc": """ O planhèm, avèm pas mai de requèstas cap al servici metèo. Vaquí las prevision metèo de l'emplaçament per defaut (per vos donar un apercebut). Poirem lèu ne far de novèlas. Podètz seguir https://x.com/igor_chubin per demorar informat. ====================================================================================== """, "fy": """ Excuses, wy kinne op dit moment 't waarberjocht net sjin litte. Hjir is 't waarberjocht foar de standaard stêd. Wy losse dit probleem sa gau mooglik op. Foar updates kinne jo ús op https://x.com/igor_chubin folgje. ====================================================================================== """, "pl": """ Bardzo nam przykro, ale chwilowo wykorzystaliśmy limit zapytań do serwisu pogodowego. To, co widzisz jest przykładowym raportem pogodowym dla domyślnego miasta. Postaramy się przywrócić funkcjonalność tak szybko, jak to tylko możliwe. Możesz śledzić https://x.com/igor_chubin na Twitterze, aby być na bieżąco. ====================================================================================== """, "pt": """ Desculpe-nos, estamos atingindo o limite de consultas ao serviço de previsão do tempo neste momento. Veja a seguir a previsão do tempo para uma cidade padrão (apenas para você ver que aspecto o relatório tem). Em breve voltaremos a ter acesso às consultas. Você pode seguir https://x.com/igor_chubin para acompanhar a situação. ====================================================================================== """, "pt-br": """ Desculpe-nos, atingimos o limite de consultas ao serviço de previsão do tempo neste momento. Veja a seguir a previsão do tempo para uma cidade padrão (apenas para você ver que aspecto o relatório tem). Em breve voltaremos a ter acesso às consultas. Você pode seguir https://x.com/igor_chubin para acompanhar a situação. ====================================================================================== """, "ro": """ Ne pare rău, momentan am epuizat cererile alocate de către serviciul de prognoză meteo. Vă arătăm prognoza meteo pentru localitatea implicită (ca exemplu, să vedeți cum arată). Vom obține alocarea de cereri noi cât de curând posibil. Puteți urmări https://x.com/igor_chubin pentru actualizări. ====================================================================================== """, "te": """ క్షమించండి, ప్రస్తుతానికి మేము వాతావరణ సేవకు ప్రశ్నలను గడుపుతున్నాం. ఇక్కడ డిఫాల్ట్ నగరం కోసం వాతావరణ నివేదిక (కేవలం మీకు చూపించడానికి, ఇది ఎలా కనిపిస్తుంది). సాధ్యమైనంత త్వరలో కొత్త ప్రశ్నలను పొందుతారు. నవీకరణల కోసం https://x.com/igor_chubin ను మీరు అనుసరించవచ్చు. ====================================================================================== """, "tr": """ Üzgünüz, an itibariyle hava durumu servisine yapabileceğimiz sorgu limitine ulaştık. Varsayılan şehir için hava durumu bilgisini görüyorsunuz (neye benzediğini gösterebilmek için). Mümkün olan en kısa sürede servise yeniden sorgu yapmaya başlayacağız. Gelişmeler için https://x.com/igor_chubin adresini takip edebilirsiniz. ====================================================================================== """, "th": """ ขออภัย การเรียกค้นหาระบบสภาพอากาศของเราหมดชั่วคราว เราจึงแสดงข้อมูลของเมืองตัวอย่าง (เพื่อที่จะแสดงให้คุณเห็นว่าหน้าตาเป็นยังไง) เราจะเรียกค้นหาใหม่โดยเร็วที่สุด คุณสามารถติดตามการอัพเดทได้ที่ https://x.com/igor_chubin ====================================================================================== """, "da": """ Beklager, men vi er ved at løbe tør for forespørgsler til vejr-servicen lige nu. Her er vejr rapporten for standard byen (bare så du ved hvordan det kan se ud). Vi får nye forespørsler hurtigst muligt. Du kan følge https://x.com/igor_chubin for at få opdateringer. ====================================================================================== """, "et": """ Vabandage, kuid hetkel on päringud ilmateenusele piiratud. Selle asemel kuvame hetkel näidislinna ilmaprognoosi (näitamaks, kuidas see välja näeb). Üritame probleemi lahendada niipea kui võimalik. Jälgige https://x.com/igor_chubin värskenduste jaoks. ====================================================================================== """, "uk": """ Вибачте, ми перевищили максимальну кількість запитів до сервісу погоди. Ось прогноз погоди у нашому місті (просто показати Вам як це виглядає). Ми відновимо роботу як тільки зможемо. Ви можете підписатися на https://x.com/igor_chubin для отримання новин. ====================================================================================== """, "vi": """ Xin lỗi, hiện tại chúng tôi đã hết lượt yêu cầu thông tin thời tiết. Đây là dự báo thời tiết cho thành phố mặc định (chỉ để cho bạn thấy nó trông như thế nào). Chung tôi sẽ có thêm lượt truy vấn sớm nhất có thể Bạn có thể theo dõi https://x.com/igor_chubin để cập nhật thông tin mới nhất. ====================================================================================== """, "zh-tw": """ 抱歉,目前天氣服務的查詢請求太多了。 這裡是預設城市的天氣報告(只是為您展示它的外觀)。 我們將盡快取得新的資料。 您可以追蹤 https://x.com/igor_chubin 以取得更新。 ====================================================================================== """, "mg": """ Miala tsiny fa misedra olana ny sampan-draharaha momba ny toetrandro amin'izao fotoana izao. Ity ny tatitra momba ny toetr'andro ho an'ny tanàna mahazatra (mba hampisehoana anao ny endriny). Haivaly aminao haingana ny fangatahanao. Azonao atao ny manaraka ny pejy https://x.com/igor_chubin. """, "ta": """ மன்னிக்கவும், தற்போது வானிலை சேவைக்கான வினவல்கள் எங்களிடம் இல்லை. இயல்புநிலை நகரத்திற்கான வானிலை அறிக்கை இதோ (அது எப்படி இருக்கும் என்பதை உங்களுக்குக் காண்பிப்பதற்காக). கூடிய விரைவில் புதிய வினவல்களைப் பெறுவோம். புதுப்பிப்புகளுக்கு நீங்கள் https://x.com/igor_chubin ஐப் பின்தொடரலாம். """, }, # Historical messages: # 'Check new Feature: \033[92mwttr.in/Moon\033[0m or \033[92mwttr.in/Moon@2016-Mar-23\033[0m to see the phase of the Moon' # 'New feature: \033[92mwttr.in/Rome?lang=it\033[0m or \033[92mcurl -H "Accept-Language: it" wttr.in/Rome\033[0m for the localized version. Your lang instead of "it"' "NEW_FEATURE": { "en": "New feature: multilingual location names \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) and location search \033[92mwttr.in/~Kilimanjaro\033[0m (just add ~ before)", "am": "አዲስ ባህሪ: የአካባቢ ስሞች በተለያዩ ቋንቋዎች \033[92mwttr.in/ኢትዮጵያ\033[0m (በ UTF-8) እና የአካባቢ ፍለጋ \033[92mwttr.in/~Kilimanjaro\033[0m (ከአካባቢ ስም በፊት ~ ያክሉ)", "ar": "ميزة جديدة : أسماء الأماكن بلغات متعددة \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) والبحث عن الأماكن \033[92mwttr.in/~Kilimanjaro\033[0m (فقط أضف ~ قبل)", "af": "Nuwe eienskap: veeltalige name vir liggings \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) en ligging soek \033[92mwttr.in/~Kilimanjaro\033[0m (plaas net ~ vooraan)", "be": "Новыя магчымасці: назвы месц на любой мове \033[92mwttr.in/станция+Восток\033[0m (в UTF-8) i пошук месц \033[92mwttr.in/~Kilimanjaro\033[0m (трэба дадаць ~ ў пачатак)", "bg": "Нова функционалност: многоезични имена на места\033[92mwttr.in/станция+Восток\033[0m (в UTF-8) и в търсенето \033[92mwttr.in/~Kilimanjaro\033[0m (добавете ~ преди)", "bn": "নতুন ফিচার : বহুভাষিক অবস্থানের নাম \ 033 [92mwttr.in/станция+Восток\033 [0m (UTF-8)] এবং অবস্থান অনুসন্ধান \ 033 [92mwttr.in/~Kilimanjaro\033 [0m (শুধু আগে ~ যোগ করুন)", "bs": "XXXXXXXXXXXXXXXXXXXX: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX\033[92mwttr.in/станция+Восток\033[0m (XX UTF-8) XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "ca": "Noves funcionalitats: noms d'ubicació multilingües \033[92mwttr.in/станция+Восток\033[0m (en UTF-8) i la ubicació de recerca \033[92mwttr.in/~Kilimanjaro\033[0m (només cal afegir ~ abans)", "cs": "Nová funkce: vícejazyčná jména poloh \033[92mwttr.in/станция+Восток\033[0m (v UTF-8) a vyhledávání polohy \033[92mwttr.in/~Kilimanjaro\033[0m (pouze přidejte ~ před pozici)", "es": "Nuevas funcionalidades: los nombres de las ubicaciones en varios idiomas \033[92mwttr.in/станция+Восток\033[0m (em UTF-8) y la búsqueda por ubicaciones \033[92mwttr.in/~Kilimanjaro\033[0m (tan solo inserte ~ al principio)", "fa": "قابلیت جدید: پشتیبانی از نام چند زبانه مکانها \033[92mwttr.in/станция+Восток\033[0m (در فرمت UTF-8) و جسجتوی مکان ها \033[92mwttr.in/~Kilimanjaro\033[0m (فقط قبل از اون ~ اضافه کنید)", "fr": "Nouvelles fonctionnalités: noms d'emplacements multilingues \033[92mwttr.in/станция+Восток\033[0m (en UTF-8) et recherche d'emplacement \033[92mwttr.in/~Kilimanjaro\033[0m (ajouter ~ devant)", "gl": "Nova funcionalidade: nomes de localizacións en varios idiomas\033[92mwttr.in/станция+Восток\033[0m (en UTF-8) e procuras de localizacións \033[92mwttr.in/~Kilimanjaro\033[0m (engade ~ antes)", "mk": "Нова функција: повеќе јазично локациски имиња \033[92mwttr.in/станция+Восток\033[0m (во UTF-8) и локациско пребарување \033[92mwttr.in/~Kilimanjaro\033[0m (just add ~ before)", "nb": "Ny funksjon: flerspråklige stedsnavn \033[92mwttr.in/станция+Восток\033[0m (i UTF-8) og lokasjonssøk \033[92mwttr.in/~Kilimanjaro\033[0m (bare legg til ~ foran)", "nl": "Nieuwe functie: tweetalige locatie namen \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) en locatie zoeken \033[92mwttr.in/~Kilimanjaro\033[0m (zet er gewoon een ~ voor)", "fy": "Nije funksje: twatalige lokaasje nammen \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) en lokaasje sykjen \033[92mwttr.in/~Kilimanjaro\033[0m (set er gewoan in ~ foar)", "cy": "Nodwedd newydd: enwau lleoliadau amlieithog \033[92mwttr.in/станция+Восток\033[0m (yn UTF-8) a chwilio am leoliad \033[92mwttr.in/~Kilimanjaro\033[0m (ychwanegwch ~ yn gyntaf)", "de": "Neue Funktion: mehrsprachige Ortsnamen \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) und Ortssuche \033[92mwttr.in/~Kilimanjaro\033[0m (fügen Sie ein ~ vor dem Ort ein)", "hi": "नई सुविधा: बहुभाषी स्थान के नाम \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) और स्थान खोज \033[92mwttr.in/~Kilimanjaro\033[0m (बस ~ आगे लगाये)", "hu": "Új funkcinalitás: többnyelvű helynevek \033[92mwttr.in/станция+Восток\033[0m (UTF-8-ban) és pozíció keresés \033[92mwttr.in/~Kilimanjaro\033[0m (csak adj egy ~ jelet elé)", "hy": "Փորձարկեք: տեղամասերի անունները կամայական լեզվով \033[92mwttr.in/Դիլիջան\033[0m (в UTF-8) և տեղանքի որոնում \033[92mwttr.in/~Kilimanjaro\033[0m (հարկավոր է ~ ավելացնել դիմացից)", "ia": "Nove functione: location nomine multilingue \033[92mwttr.in/станция+Восток\033[0m (a UTF-8) e recerca de location\033[92mwttr.in/~Kilimanjaro\033[0m (solo adde ~ ante)", "id": "Fitur baru: nama lokasi dalam multibahasa \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) dan pencarian lokasi \033[92mwttr.in/~Kilimanjaro\033[0m (hanya tambah tanda ~ sebelumnya)", "it": "Nuove funzionalità: nomi delle località multilingue \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) e ricerca della località \033[92mwttr.in/~Kilimanjaro\033[0m (basta premettere ~)", "ko": "새로운 기능: 다국어로 대응된 위치 \033[92mwttr.in/서울\033[0m (UTF-8에서) 장소 검색 \033[92mwttr.in/~Kilimanjaro\033[0m (앞에 ~를 붙이세요)", "kk": "", "lt": "Naujiena: daugiakalbiai vietovių pavadinimai \033[92mwttr.in/станция+Восток\033[0m (UTF-8) ir vietovių paieška \033[92mwttr.in/~Kilimanjaro\033[0m (tiesiog priekyje pridėkite ~)", "lv": "Jauna iespēja: daudzvalodu atrašanās vietu nosaukumi \033[92mwttr.in/станция+Восток\033[0m (UTF-8) un atrašanās vietu meklēšana \033[92mwttr.in/~Kilimanjaro\033[0m (sākumā tikai jāpievieno ~)", "mk": "Нова функција: повеќе јазично локациски имиња \033[92mwttr.in/станция+Восток\033[0m (во UTF-8) и локациско пребарување \033[92mwttr.in/~Kilimanjaro\033[0m (just add ~ before)", "mr": "नवीन वैशिष्ट्य: स्थळांची बहुभाषिक नावे \033[92mwttr.in/станция+Восток\033[0m (UTF-8 मध्ये) आणि स्थळ शोध \033[92mwttr.in/~Kilimanjaro\033[0m (फक्त आधी ~ जोडा)", "oc": "Novèla foncionalitat : nom de lòc multilenga \033[92mwttr.in/станция+Восток\033[0m (en UTF-8) e recèrca de lòc \033[92mwttr.in/~Kilimanjaro\033[0m (solament ajustatz ~ abans)", "pl": "Nowa funkcjonalność: wielojęzyczne nazwy lokalizacji \033[92mwttr.in/станция+Восток\033[0m (w UTF-8) i szukanie lokalizacji \033[92mwttr.in/~Kilimanjaro\033[0m (poprzedź zapytanie ~ - znakiem tyldy)", "pt": "Nova funcionalidade: nomes de localidades em várias línguas \033[92mwttr.in/станция+Восток\033[0m (em UTF-8) e procura por localidades \033[92mwttr.in/~Kilimanjaro\033[0m (é só colocar ~ antes)", "pt-br": "Nova funcionalidade: nomes de localidades em várias línguas \033[92mwttr.in/станция+Восток\033[0m (em UTF-8) e procura por localidades \033[92mwttr.in/~Kilimanjaro\033[0m (é só colocar ~ antes)", "ro": "Funcționalitate nouă: nume de localități multilingve \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) și căutare de localități \033[92mwttr.in/~Kilimanjaro\033[0m (adăuați ~ în față)", "ru": "Попробуйте: названия мест на любом языке \033[92mwttr.in/станция+Восток\033[0m (в UTF-8) и поиск мест \033[92mwttr.in/~Kilimanjaro\033[0m (нужно добавить ~ спереди)", "zh": "新功能:多语言地点名称 \033[92mwttr.in/станция+Восток\033[0m (in UTF-8) 及地点搜索\033[92mwttr.in/~Kilimanjaro\033[0m (只需在名称前加~)", "tr": "Yeni özellik: çok dilli konum isimleri \033[92mwttr.in/станция+Восток\033[0m (UTF-8 ile) ve konum arama \033[92mwttr.in/~Kilimanjaro\033[0m (sadece önüne ~ ekleyin)", "te": "క్రొత్త లక్షణం: బహుభాషా స్థాన పేర్లు \ 033 [92mwttr.in/stancelя+Vostок\033 [0 U (UTF-8 లో) మరియు స్థానం శోధన \ 033 [92mwttr.in/~kilimanjaro\033 [0m (కేవలం ~ ముందుకి జోడించండి)", "th": "ฟีเจอร์ใหม่: แสดงที่ตั้งได้หลายภาษา \033[92mwttr.in/станция+Восток\033[0m (ใน UTF-8) และการค้นหาที่ตั้ง \033[92mwttr.in/~Kilimanjaro\033[0m (เพียงเพิ่ม ~ ข้างหน้า)", "da": "Ny funktion: flersprogede lokationsnavne \033[92mwttr.in/станция+Восток\033[0m (som UTF-8) og lokations søgning \033[92mwttr.in/~Kilimanjaro\033[0m (bare tilføj ~ inden)", "et": "Uus funktsioon: mitmekeelsed asukohanimed \033[92mwttr.in/станция+Восток\033[0m (UTF-8 vormingus) ja asukoha otsing \033[92mwttr.in/~Kilimanjaro\033[0m (lisa ~ enne)", "uk": "Спробуйте: назви місць будь-якою мовою \033[92mwttr.in/станція+Восток\033[0m (в UTF-8) та пошук місць \033[92mwttr.in/~Kilimanjaro\033[0m (потрібно додати ~ спочатку)", "vi": "Chức năng mới: tên địa điểm đa ngôn ngữ \033[92mwttr.in/станция+Восток\033[0m (dùng UTF-8) và tìm kiếm địa điểm \033[92mwttr.in/~Kilimanjaro\033[0m (chỉ cần thêm ~ phía trước)", "zh-tw": "新功能:多語言地點名稱 \033[92mwttr.in/станция+Восток\033[0m (使用 UTF-8 編碼)與位置搜尋 \033[92mwttr.in/~Kilimanjaro\033[0m (只要在地點前加 ~ 就可以了)", "mg": "Fanatsrana vaovao: anarana toerana amin'ny fiteny maro\033[92mwttr.in/станция+Восток\033[0m (en UTF-8) sy fitadiavana toerana \033[92mwttr.in/~Kilimanjaro\033[0m (ampio ~ fotsiny eo aloha)", "ta": "புதிய அம்சம்: பன்மொழி இருப்பிடப் பெயர்கள் \033[92mwttr.in/станция+Восток\033[0m (UTF-8 இல்) மற்றும் இருப்பிடத் தேடல் \033[92mwttr.in/~Kilimanjaro\033[0m (முன் ~ஐச் சேர்க்கவும்)", }, "FOLLOW_ME": { "en": "Follow \033[46m\033[30m@igor_chubin\033[0m for wttr.in updates", "ar": "لآخر المستجدات تابع \033[46m\033[30m@igor_chubin\033[0m", "af": "Volg \033[46m\033[30m@igor_chubin\033[0m vir wttr.in opdaterings", "am": "ለተጨማሪ wttr.in ዜና እና መረጃ \033[46m\033[30m@igor_chubin\033[0m ን ይከተሉ", "be": "Сачыце за \033[46m\033[30m@igor_chubin\033[0m за навінамі wttr.in", "bg": "Последвай \033[46m\033[30m@igor_chubin\033[0m за обновления свързани с wttr.in", "bn": "wttr.in আপডেটের জন্য \033[46m\033[30m@igor_chubin\033[0m কে অনুসরণ করুন", "bs": "XXXXXX \033[46m\033[30m@igor_chubin\033[0m XXXXXXXXXXXXXXXXXXX", "ca": "Segueix \033[46m\033[30m@igor_chubin\033[0m per actualitzacions de wttr.in", "cs": "Sledujte \033[46m\033[30m@igor_chubin\033[0m pro aktualizace pro wttr.in", "es": "Sigue a \033[46m\033[30m@igor_chubin\033[0m para enterarte de las novedades de wttr.in", "eu": "\033[46m\033[30m@igor_chubin\033[0m jarraitu wttr.in berriak jasotzeko", "cy": "Dilyner \033[46m\033[30m@igor_Chubin\033[0m am diweddariadau wttr.in", "fa": "برای دنبال کردن خبرهای wttr.in شناسه \033[46m\033[30m@igor_chubin\033[0m رو فالو کنید.", "fr": "Suivez \033[46m\033[30m@igor_Chubin\033[0m pour rester informé sur wttr.in", "de": "Folgen Sie \033[46m\033[30mhttps://x.com/igor_chubin\033[0m für wttr.in Updates", "ga": "Lean \033[46m\033[30m@igor_chubin\033[0m don wttr.in eolas is deanaí", "gl": "Segue a \033[46m\033[30m@igor_chubin\033[0m para actualizacións sobre wttr.in", "hi": "अपडेट के लिए फॉलो करें \033[46m\033[30m@igor_chubin\033[0m", "hu": "Kövesd \033[46m\033[30m@igor_chubin\033[0m-t további wttr.in információkért", "hy": "Նոր ֆիչռների համար հետևեք՝ \033[46m\033[30m@igor_chubin\033[0m", "ia": "Seque \033[46m\033[30m@igor_chubin\033[0m por nove information de wttr.in", "id": "Ikuti \033[46m\033[30m@igor_chubin\033[0m untuk informasi wttr.in terbaru", "it": "Seguite \033[46m\033[30m@igor_chubin\033[0m per aggiornamenti a wttr.in", "ko": "wttr.in의 업데이트 소식을 원하신다면 \033[46m\033[30m@igor_chubin\033[0m 을 팔로우 해주세요", "kk": "", "lt": "wttr.in atnaujinimus sekite \033[46m\033[30m@igor_chubin\033[0m", "lv": "Seko \033[46m\033[30m@igor_chubin\033[0m, lai uzzinātu jaunumus par wttr.in", "mk": "Следете \033[46m\033[30m@igor_chubin\033[0m за wttr.in новости", "mr": "wttr.in च्या अद्यावत माहितीसाठी \033[46m\033[30m@igor_chubin\033[0m चे अनुसरण करा", "nb": "Følg \033[46m\033[30m@igor_chubin\033[0m for wttr.in oppdateringer", "nl": "Volg \033[46m\033[30m@igor_chubin\033[0m voor wttr.in updates", "oc": "Seguissètz \033[46m\033[30m@igor_Chubin\033[0m per demorar informat sus wttr.in", "fy": "Folgje \033[46m\033[30m@igor_chubin\033[0m foar wttr.in updates", "pl": "Śledź \033[46m\033[30m@igor_chubin\033[0m aby być na bieżąco z nowościami dotyczącymi wttr.in", "pt": "Seguir \033[46m\033[30m@igor_chubin\033[0m para as novidades de wttr.in", "pt-br": "Seguir \033[46m\033[30m@igor_chubin\033[0m para as novidades de wttr.in", "ro": "Urmăriți \033[46m\033[30m@igor_chubin\033[0m pentru actualizări despre wttr.in", "ru": "Все новые фичи публикуются здесь: \033[46m\033[30m@igor_chubin\033[0m", "zh": "关注 \033[46m\033[30m@igor_chubin\033[0m 获取 wttr.in 动态", "te": "అనుసరించండి \ 033 [46m \ 033 [30m @ igor_chubin \ 033 [wttr.in నవీకరణలను కోసం", "tr": "wttr.in ile ilgili gelişmeler için \033[46m\033[30m@igor_chubin\033[0m adresini takip edin", "th": "ติดตาม \033[46m\033[30m@igor_chubin\033[0m สำหรับการอัพเดท wttr.in", "da": "Følg \033[46m\033[30m@igor_chubin\033[0m for at få wttr.in opdateringer", "et": "Jälgi \033[46m\033[30m@igor_chubin\033[0m wttr.in uudiste tarbeks", "uk": "Нові можливості wttr.in публікуються тут: \033[46m\033[30m@igor_chubin\033[0m", "vi": "Theo dõi \033[46m\033[30m@igor_chubin\033[0m để cập nhật thông tin về wttr.in", "zh-tw": "追蹤 \033[46m\033[30m@igor_chubin\033[0m 以取得更多 wttr.in 的動態", "mg": "Araho ao ny pejy \033[46m\033[30m@igor_Chubin\033[0m raha toa ka te hahazo vaovao momban'ny wttr.in", "ta": "wttr.in புதுப்பிப்புகளுக்கு \033[46m\033[30m@igor_chubin\033[0m ஐப் பின்தொடரவும்", }, } CAPTION = { "af": "Weer verslag vir:", "am": "የአየር ሁኔታ ሪፖርት ለ", "ar": "تقرير حالة الطقس", "az": "Hava proqnozu:", "be": "Прагноз надвор'я для:", "bg": "Прогноза за времето в:", "bn": "আবহাওয়ার প্রতিবেদন:", "bs": "Vremenske prognoze za:", "ca": "Informe del temps per a:", "cs": "Předpověď počasí pro:", "cy": "Adroddiad tywydd ar gyfer:", "da": "Vejret i:", "de": "Wetterbericht für:", "el": "Πρόγνωση καιρού για:", "en": "Weather report for:", "eo": "Veterprognozo por:", "es": "Pronóstico del tiempo en:", "et": "Ilmaprognoos:", "eu": "Eguraldiaren iragarpena:", "fa": "گزارش آب و هئا برای شما:", "fi": "Säätiedotus:", "fr": "Prévisions météo pour:", "fy": "Waarberjocht foar:", "ga": "Réamhaisnéis na haimsire do:", "he": ":ריוואה גזמ תיזחת", "hi": "मौसम की जानकारी", "hr": "Vremenska prognoza za:", "hu": "Időjárás előrejelzés:", "hy": "Եղանակի տեսություն:", "ia": "Reporto tempore por:", "id": "Prakiraan cuaca:", "it": "Previsioni meteo:", "is": "Veðurskýrsla fyrir:", "ja": "天気予報:", "jv": "Weather forecast for:", "ka": "ამინდის პროგნოზი:", "kk": "Ауа райы:", "ko": "일기 예보:", "ky": "Аба ырайы:", "lt": "Orų prognozė:", "lv": "Laika ziņas:", "mk": "Прогноза за времето во:", "ml": "കാലാവസ്ഥ റിപ്പോർട്ട്:", "mr": "हवामान अहवाल:", "nb": "Værmelding for:", "nl": "Weerbericht voor:", "nn": "Vêrmelding for:", "oc": "Previsions metèo per :", "pl": "Pogoda w:", "pt": "Previsão do tempo para:", "pt-br": "Previsão do tempo para:", "ro": "Prognoza meteo pentru:", "ru": "Прогноз погоды:", "sk": "Predpoveď počasia pre:", "sl": "Vremenska napoved za", "sr": "Временска прогноза за:", "sr-lat": "Vremenska prognoza za:", "sv": "Väderleksprognos för:", "sw": "Ripoti ya hali ya hewa, jiji la:", "te": "వాతావరణ సమాచారము:", "th": "รายงานสภาพอากาศ:", "tr": "Hava beklentisi:", "uk": "Прогноз погоди для:", "uz": "Ob-havo bashorati:", "vi": "Báo cáo thời tiết:", "zh": "天气预报:", "zu": "Isimo sezulu:", "zh-tw": "天氣報告:", "mg": "Toetr'andro any :", "ta": "வானிலை அறிக்கை:", } def get_message(message_name, lang): if lang == "zh-cn": lang = "zh" if message_name not in MESSAGE: return "" message_dict = MESSAGE[message_name] return message_dict.get(lang, message_dict.get("en", "")) ================================================ FILE: lib/translations_v2.py ================================================ # vim: fileencoding=utf-8 """ Translation of v2 """ # pylint: disable=line-too-long,bad-whitespace V2_TRANSLATION = { "en": ( "Weather report for:", "Weather", "Timezone", "Now", "Dawn", "Sunrise", "Zenith", "Sunset", "Dusk", ), "af": ("Weer verslag vir:", "", "", "", "", "", "", "", ""), "am": ( "የአየር ሁኔታ ዘገባ ለ ፥", "የአየር ሁኔታ", "የጊዜ ሰቅ", "አሁን", "ንጋት", "የፀሐይ መውጫ", "", "የፀሐይ መጥለቅ", "ምሽት", ), "ar": ( "تقرير حالة الطقس:", "حالة الطقس", "المنطقة الزمنية", "الآن ", "الفجر", "شروق الشمس", "الذروة", "غروب الشمس", "الغسق", ), "az": ( "Hava proqnozu:", "Hava", "Saat zonası", "İndi", "Şəfəq", "Günəş çıxdı", "Zenit", "Gün batımı", "Toran", ), "be": ( "Прагноз надвор'я для:", "Надвор'е", "Часавая зона", "Цяпер", "Світанак", "Усход сонца", "Зеніт", "Захад сонца", "Змярканне", ), "bg": ("Прогноза за времето в:", "", "", "", "", "", "", "", ""), "bn": ( "আবহাওয়া প্রতিবেদন:", "আবহাওয়া", "টাইমজোন", "এখন", "ভোর", "সূর্যোদয়", "সুবিন্দু", "সূর্যাস্ত", "সন্ধ্যা", ), "bs": ("Vremenske prognoze za:", "", "", "", "", "", "", "", ""), "ca": ( "Informe del temps per a:", "Oratge", "Zona horària", "Ara", "Albada", "Sortida", "Zenit", "Posta", "Crepuscle", ), "cs": ( "Předpověď počasí pro:", "Počasí", "Časové pásmo", "Nyní", "Svítání", "Východ slunce", "Vrchol", "Západ slunce", "Soumrak" ), "cy": ("Adroddiad tywydd ar gyfer:", "", "", "", "", "", "", "", ""), "da": ( "Vejret i:", "Vejret", "Tidszone", "Nu", "Daggry", "Solopgang", "Zenit", "Solnedgang", "Skumring", ), "de": ( "Wetterbericht für:", "Wetter", "Zeitzone", "Jetzt", "Morgendämmerung", "Sonnenaufgang", "Zenit", "Sonnenuntergang", "Abenddämmerung", ), "el": ("Πρόγνωση καιρού για:", "", "", "", "", "", "", "", ""), "eo": ( "Veterprognozo por:", "Vetero", "Horzono", "Nun", "Tagiĝo", "Sunleviĝo", "Zenito", "Sunsubiro", "Krepusko", ), "es": ( "El tiempo en:", "Clima", "Zona horaria", "Ahora", "Alborada", "Amanecer", "Cenit", "Atardecer", "Anochecer", ), "et": ( "Ilmaprognoos:", "Ilm", "Ajatsoon", "Hetkel", "Koit", "Päikesetõus", "Seniit", "Päikeseloojang", "Eha", ), "eu": ( "Eguraldia:", "Ordu-eremua", "Orain", "Egunsentia", "Eguzkia", "Zenit", "Ilunabarra", "Ilunabarra", ), "fa": ("گزارش وضعیت آب و هوا", "", "", "", "", "", "", "", ""), "fi": ("Säätiedotus:", "", "", "", "", "", "", "", ""), "fr": ( "Prévisions météo pour :", "Météo", "Fuseau Horaire", "Heure", "Aube", "Lever du Soleil", "Zénith", "Coucher du Soleil", "Crépuscule", ), "fy": ("Waarberjocht foar:", "", "", "", "", "", "", "", ""), "ga": ( "Réamhaisnéis na haimsire do:", "Aimsir", "Crios ama", "Anois", "Breacadh an lae", "Éirí na gréine", "Forar", "Dul faoi na gréine", "Coineascar", ), "he": (":ריוואה גזמ תיזחת", "", "", "", "", "", "", "", ""), "hi": ( "मौसम की जानकारी", "मौसम", "समय मण्डल", "अभी", "उदय", "सूर्योदय", "चरम बिन्दु", "सूर्यास्त", "संध्याकाल", ), "hr": ("Vremenska prognoza za:", "", "", "", "", "", "", "", ""), "hu": ( "Időjárás előrejelzés:", "Időjárás", "időzóna", "aktuális", "hajnal", "napkelte", "dél", "naplemente", "szürkület", ), "hy": ("Եղանակի տեսություն:", "", "", "", "", "", "", "", ""), "ia": ( "Reporto tempore pro:", "Tempore", "Fuso Horari", "Alora", "Alba", "Aurora", "Zenit", "Poner del sol", "Crepusculo", ), "id": ("Prakiraan cuaca:", "", "", "", "", "", "", "", ""), "it": ( "Previsioni meteo:", "Tempo", "Fuso orario", "Ora", "Alba", "Sorgere del Sole", "Zenit", "Tramonto", "Crepuscolo", ), "is": ("Veðurskýrsla fyrir:", "", "", "", "", "", "", "", ""), "ja": ("天気予報:", "天気", "タイムゾーン", "今", "夜明け", "日の出", "天頂", "日の入", "日暮れ"), "jv": ("Weather forecast for:", "", "", "", "", "", "", "", ""), "ka": ("ამინდის პროგნოზი:", "", "", "", "", "", "", "", ""), "kk": ("Ауа райы:", "", "", "", "", "", "", "", ""), "ko": ("일기 예보:", "날씨", "시간대", "현재", "새벽", "일출", "정오", "일몰", "황혼"), "ky": ("Аба ырайы:", "", "", "", "", "", "", "", ""), "lt": ( "Orų prognozė:", "Orai", "Laiko zona", "Dabar", "Aušra", "Saulėtekis", "Zenitas", "Saulėlydis", "Sutemos", ), "lv": ( "Laika ziņas par:", "laikapstākļiem", "laika joslu", "tagad", "rītausmu", "saullēktu", "zenītu", "saulrietu", "vakara krēslu", ), "mk": ("Прогноза за времето во:", "", "", "", "", "", "", "", ""), "ml": ( "കാലാവസ്ഥ റിപ്പോർട്ട്:", "കാലാവസ്", "സമയ മേഖല", "ഇപ്പോൾ", "പ്രഭാതത്തെ", "ഉച്ചതിരിഞ്ഞ്", "സൂര്യോദയം", "പരമോന്നത", "സൂര്യാസ്തമയം", "സന്ധ്യ", ), "mr": ( "हवामान अहवालाचे ठिकाण:", "हवामान", "कालक्षेत्र", "आता", "पहाट", "सूर्योदय", "शिखरबिंदु", "सूर्यास्त", "संध्याकाळ", ), "nb": ( "Værmelding for:", "Vær", "Tidssone", "Nå", "Morgengry", "Soloppgang", "Solhøyde", "Solnedgang", "Skumring", ), "nl": ( "Weerbericht voor:", "Weer", "Tijdzone", "Nu", "Dageraad", "Zonsopkomst", "Zenit", "Zonsondergang", "Schemering", ), "nn": ("Vêrmelding for:", "", "", "", "", "", "", "", ""), "oc": ( "Previsions metèo per:" "Metèo", "Zòna orària", "Ara", "Auròra", "Alba", "Zenit", "A solelh colc", "Entrelutz", ), "pl": ("Pogoda w:", "", "", "", "", "", "", "", ""), "pt": ( "Previsão do tempo para:", "Tempo", "Fuso horário", "Agora", "Alvorada", "Nascer do sol", "Zénite", "Pôr do sol", "Crepúsculo", ), "pt-br": ( "Previsão do tempo para:", "Tempo", "Fuso horário", "Agora", "Alvorada", "Nascer do sol", "Zénite", "Pôr do sol", "Crepúsculo", ), "ro": ("Prognoza meteo pentru:", "", "", "", "", "", "", "", ""), "ru": ( "Прогноз погоды:", "Погода", "Часовой пояс", "Сейчас", "Рассвет", "Восход", "Зенит", "Закат", "Сумерки", ), "sk": ("Predpoveď počasia pre:", "", "", "", "", "", "", "", ""), "sl": ("Vremenska napoved za", "", "", "", "", "", "", "", ""), "sr": ("Временска прогноза за:", "", "", "", "", "", "", "", ""), "sr-lat": ("Vremenska prognoza za:", "", "", "", "", "", "", "", ""), "sv": ("Väderleksprognos för:", "", "", "", "", "", "", "", ""), "sw": ("Ripoti ya hali ya hewa, jiji la:", "", "", "", "", "", "", "", ""), "te": ( "వాతావరణ సమాచారము:", "వాతావరణం", "కాల మండలం", "ప్రస్తుతం", "తెల్లవారుజాము", "సూర్యోదయం", "ఉన్నత స్థానం", "సూర్యాస్తమయం", "సందెచీకటి", ), "th": ("รายงานสภาพอากาศ:", "", "", "", "", "", "", "", ""), "tr": ( "Hava beklentisi:", "Hava Durumu", "Zaman Dilimi", "Şimdi", "Şafak", "Gün Doğumu", "Doruk", "Gün Batımı", "Akşam", ), "uk": ( "Прогноз погоди для:", "Погода", "Часовий пояс", "Зараз", "Світанок", "Схід сонця", "Зеніт", "Захід сонця", "Сутінки", ), "uz": ("Ob-havo bashorati:", "", "", "", "", "", "", "", ""), "vi": ("Báo cáo thời tiết:", "", "", "", "", "", "", "", ""), "zh": ("天气预报:", "天气", "时区", "当前", "黎明", "日出", "正午", "日落", "黄昏"), "zh-tw": ("天氣預報:", "天氣", "時區", "目前", "黎明", "日出", "日正當中", "日落", "黃昏"), "zu": ("Isimo sezulu:", "", "", "", "", "", "", "", ""), "mg": ( "Vinavina toetr'andro hoany :", "Toetr'andro", "Faritra ora", "Ora", "Mangirandratsy", "Maneno akoho", "Mitatao vovonana", "Masoandro milentika", "Crépuscule", ), "ta": ( "வானிலை அறிக்கை:", "வானிலை", "நேரம் மண்டலம்", "இப்போது", "விடியல்", "சூரிய உதயம்", "ஜெனித்", "சூரிய அஸ்தமனம்", "அந்தி", ), } ================================================ FILE: lib/view/__init__.py ================================================ ================================================ FILE: lib/view/line.py ================================================ # vim: fileencoding=utf-8 """ One-line output mode. Initial implementation of one-line output mode. [ ] forecast [ ] spark [ ] several locations [ ] location handling [ ] more preconfigured format lines [ ] add information about this mode to /:help """ import sys import re import datetime import json import requests from astral import LocationInfo from astral import moon from astral.sun import sun import pytz from constants import ( WWO_CODE, WEATHER_SYMBOL, WEATHER_SYMBOL_WI_NIGHT, WEATHER_SYMBOL_WI_DAY, WIND_DIRECTION, WIND_DIRECTION_WI, WEATHER_SYMBOL_WIDTH_VTE, WEATHER_SYMBOL_PLAIN, ) from weather_data import get_weather_data from . import v2 #from . import v3 from . import prometheus PRECONFIGURED_FORMAT = { "1": r"%c %t\n", "2": r"%c 🌡️%t 🌬️%w\n", "3": r"%l: %c %t\n", "4": r"%l: %c 🌡️%t 🌬️%w\n", "69": r"nice", } MOON_PHASES = ("🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘") def convert_to_fahrenheit(temp): "Convert Celcius `temp` to Fahrenheit" return (temp * 9.0 / 5) + 32 def render_temperature(data, query): """ temperature (t) """ if query.get("use_imperial", False): temperature = "%s°F" % data["temp_F"] else: temperature = "%s°C" % data["temp_C"] if temperature[0] != "-": temperature = "+" + temperature return temperature def render_feel_like_temperature(data, query): """ feel like temperature (f) """ if query.get("use_imperial", False): temperature = "%s°F" % data["FeelsLikeF"] else: temperature = "%s°C" % data["FeelsLikeC"] if temperature[0] != "-": temperature = "+" + temperature return temperature def render_condition(data, query): """Emoji encoded weather condition (c)""" if query.get("view") == "v2n": weather_condition = WEATHER_SYMBOL_WI_NIGHT.get( WWO_CODE.get(data["weatherCode"], "Unknown") ) spaces = " " elif query.get("view") == "v2d": weather_condition = WEATHER_SYMBOL_WI_DAY.get( WWO_CODE.get(data["weatherCode"], "Unknown") ) spaces = " " else: weather_condition = WEATHER_SYMBOL.get( WWO_CODE.get(data["weatherCode"], "Unknown") ) spaces = " " * (3 - WEATHER_SYMBOL_WIDTH_VTE.get(weather_condition, 1)) return weather_condition + spaces def render_condition_fullname(data, query): """ condition_fullname (C) """ found = None for key, val in data.items(): if key.startswith("lang_"): found = val break if not found: found = data["weatherDesc"] try: weather_condition = found[0]["value"] except KeyError: weather_condition = "" return weather_condition def render_condition_plain(data, query): """Plain text weather condition (x)""" weather_condition = WEATHER_SYMBOL_PLAIN[WWO_CODE[data["weatherCode"]]] return weather_condition def render_condition_int(data, query): """Weather condition code as integer (i)""" return data["weatherCode"] def render_humidity(data, query): """ humidity (h) """ humidity = data.get("humidity", "") if humidity: humidity += "%" return humidity def render_precipitation(data, query): """ precipitation (p) """ answer = data.get("precipMM", "") if answer: answer += "mm" return answer def render_precipitation_chance(data, query): """ precipitation chance (o) """ answer = data.get("chanceofrain", "") if answer: answer += "%" return answer def render_pressure(data, query): """ pressure (P) """ answer = data.get("pressure", "") if answer: answer += "hPa" return answer def render_dewpoint(data, query): """ dew point (e) """ try: temp_c = float(data["temp_C"]) humidity = float(data["humidity"]) except (KeyError, ValueError, TypeError): return "" if not humidity: return "" dew_point_c = temp_c - (100.0 - humidity) / 5.0 if query.get("use_imperial", False): dew_point = "%s°F" % int(round(dew_point_c * 9.0 / 5 + 32)) else: dew_point = "%s°C" % int(round(dew_point_c)) if dew_point[0] != "-": dew_point = "+" + dew_point return dew_point def render_uv_index(data, query): """ UV Index (u) """ answer = data.get("uvIndex", "") return answer def render_wind(data, query): """ wind (w) """ try: degree = data["winddirDegree"] except KeyError: degree = "" try: degree = int(degree) except ValueError: degree = "" if degree: if query.get("view") in ["v2n", "v2d"]: wind_direction = WIND_DIRECTION_WI[int(((degree + 22.5) % 360) / 45.0)] else: wind_direction = WIND_DIRECTION[int(((degree + 22.5) % 360) / 45.0)] else: wind_direction = "" if query.get("use_ms_for_wind", False): unit = "m/s" wind = "%s%.1f%s" % ( wind_direction, float(data["windspeedKmph"]) / 36.0 * 10.0, unit, ) elif query.get("use_imperial", False): unit = "mph" wind = "%s%s%s" % (wind_direction, data["windspeedMiles"], unit) else: unit = "km/h" wind = "%s%s%s" % (wind_direction, data["windspeedKmph"], unit) return wind def render_location(data, query): """ location (l) """ return data["override_location"] or data["location"] def render_moonphase(_, query): """moonpahse(m) A symbol describing the phase of the moon """ moon_phase = moon.phase(date=datetime.datetime.today()) moon_index = int(int(32.0 * moon_phase / 28 + 2) % 32 / 4) return MOON_PHASES[moon_index] def render_moonday(_, query): """moonday(M) An number describing the phase of the moon (days after the New Moon) """ moon_phase = moon.phase(date=datetime.datetime.today()) return str(int(moon_phase)) ################################## # this part should be rewritten # this is just a temporary solution def get_geodata(location): # text = requests.get("http://localhost:8004/%s" % location).text text = requests.get( "http://127.0.0.1:8085/:geo-location?location=%s" % location ).text return json.loads(text) def render_dawn(data, query, local_time_of): """dawn (D) Local time of dawn""" return local_time_of("dawn") def render_dusk(data, query, local_time_of): """dusk (d) Local time of dusk""" return local_time_of("dusk") def render_sunrise(data, query, local_time_of): """sunrise (S) Local time of sunrise""" return local_time_of("sunrise") def render_sunset(data, query, local_time_of): """sunset (s) Local time of sunset""" return local_time_of("sunset") def render_zenith(data, query, local_time_of): """zenith (z) Local time of zenith""" return local_time_of("noon") def render_local_time(data, query, local_time_of): """local_time (T) Local time""" return "%{{NOW(" + local_time_of("TZ") + ")}}" def render_local_timezone(data, query, local_time_of): """local_time (Z) Local time""" return local_time_of("TZ") ################################## FORMAT_SYMBOL = { "c": render_condition, "C": render_condition_fullname, "x": render_condition_plain, "i": render_condition_int, "h": render_humidity, "t": render_temperature, "f": render_feel_like_temperature, "w": render_wind, "l": render_location, "m": render_moonphase, "M": render_moonday, "p": render_precipitation, "o": render_precipitation_chance, "P": render_pressure, "e": render_dewpoint, "u": render_uv_index, } FORMAT_SYMBOL_ASTRO = { "D": render_dawn, "d": render_dusk, "S": render_sunrise, "s": render_sunset, "z": render_zenith, "T": render_local_time, "Z": render_local_timezone, } def render_line(line, data, query): """ Render format `line` using `data` """ def get_local_time_of(): location = data["location"] geo_data = get_geodata(location) city = LocationInfo() city.latitude = geo_data["latitude"] city.longitude = geo_data["longitude"] city.timezone = geo_data["timezone"] timezone = city.timezone local_tz = pytz.timezone(timezone) datetime_day_start = datetime.datetime.now().replace( hour=0, minute=0, second=0, microsecond=0 ) current_sun = sun(city.observer, date=datetime_day_start) local_time_of = ( lambda x: city.timezone if x == "TZ" else current_sun[x] .replace(tzinfo=pytz.utc) .astimezone(local_tz) .strftime("%H:%M:%S") ) return local_time_of def render_symbol(match): """ Render one format symbol from re `match` using `data` from external scope. """ symbol_string = match.group(0) symbol = symbol_string[-1] if symbol in FORMAT_SYMBOL: render_function = FORMAT_SYMBOL[symbol] return render_function(data, query) if symbol in FORMAT_SYMBOL_ASTRO and local_time_of is not None: render_function = FORMAT_SYMBOL_ASTRO[symbol] return render_function(data, query, local_time_of) return "" template_regexp = r"%[a-zA-Z]" for template_code in re.findall(template_regexp, line): if template_code.lstrip("%") in FORMAT_SYMBOL_ASTRO: local_time_of = get_local_time_of() break return re.sub(template_regexp, render_symbol, line) def render_json(data): output = json.dumps(data, indent=4, sort_keys=True, ensure_ascii=False) output = ( "\n".join( re.sub('"[^"]*worldweatheronline[^"]*"', '""', line) if "worldweatheronline" in line else line for line in output.splitlines() ) + "\n" ) return output def format_weather_data(query, parsed_query, data): """ Format information about current weather `data` for `location` with specified in `format_line` format """ if "data" not in data: return "Unknown location; please try ~%s" % parsed_query["location"] format_line = parsed_query.get("view", "") if format_line in PRECONFIGURED_FORMAT: format_line = PRECONFIGURED_FORMAT[format_line] if format_line in ["j1", "j2"]: # j2 is a lightweight j1, without 'hourly' in 'weather' (weather forecast) if "weather" in data["data"] and format_line == "j2": for i in range(len(data["data"]["weather"])): del data["data"]["weather"][i]["hourly"] return render_json(data["data"]) if format_line == "p1": return prometheus.render_prometheus(data["data"]) if format_line[:2] == "v2": return v2.main(query, parsed_query, data) # if format_line[:2] == "v3": # return v3.main(query, parsed_query, data) current_condition = data["data"]["current_condition"][0] current_condition["location"] = parsed_query["location"] current_condition["override_location"] = parsed_query["override_location_name"] output = render_line(format_line, current_condition, query) output = output.rstrip("\n").replace(r"\n", "\n") return output def wttr_line(query, parsed_query): """ Return 1line weather information for `location` in format `line_format` """ location = parsed_query["location"] lang = parsed_query["lang"] data = get_weather_data(location, lang) output = format_weather_data(query, parsed_query, data) return output def main(): """ Function for standalone module usage """ location = sys.argv[1] query = { "line": sys.argv[2], } parsed_query = { "location": location, "orig_location": location, "language": "en", "format": "v2", } sys.stdout.write(wttr_line(query, parsed_query)) if __name__ == "__main__": main() ================================================ FILE: lib/view/moon.py ================================================ import sys import os import dateutil.parser from gevent.subprocess import Popen, PIPE sys.path.insert(0, "..") import constants import parse_query import globals def get_moon(parsed_query): location = parsed_query["orig_location"] html = parsed_query["html_output"] lang = parsed_query["lang"] hemisphere = parsed_query["hemisphere"] date = None if "@" in location: date = location[location.index("@") + 1 :] location = location[: location.index("@")] cmd = [globals.PYPHOON] if lang: cmd += ["-l", lang] if not hemisphere: cmd += ["-s", "south"] if date: try: dateutil.parser.parse(date) except Exception as e: print("ERROR: %s" % e) else: cmd += [date] p = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout = p.communicate()[0] stdout = stdout.decode("utf-8") if parsed_query.get("no-terminal", False): stdout = globals.remove_ansi(stdout) if parsed_query.get("dumb", False): stdout = stdout.translate(globals.TRANSLATION_TABLE) if html: p = Popen( ["bash", globals.ANSI2HTML, "--palette=solarized", "--bg=dark"], stdin=PIPE, stdout=PIPE, stderr=PIPE, ) stdout, stderr = p.communicate(stdout.encode("utf-8")) stdout = stdout.decode("utf-8") stderr = stderr.decode("utf-8") if p.returncode != 0: globals.error(stdout + stderr) return stdout ================================================ FILE: lib/view/prometheus.py ================================================ """ Rendering weather data in the Prometheus format. """ from datetime import datetime from fields import DESCRIPTION def _render_current(data, for_day="current", already_seen=[]): "Converts data into prometheus style format" output = [] for field_name, val in DESCRIPTION.items(): help, name = val try: value = data[field_name] if field_name == "weatherDesc": value = value[0]["value"] except (IndexError, KeyError): try: value = data["astronomy"][0][field_name] if value.endswith(" AM") or value.endswith(" PM"): value = _convert_time_to_minutes(value) except (IndexError, KeyError, ValueError): continue try: if name == "observation_time": value = _convert_time_to_minutes(value) except ValueError: continue description = "" try: float(value) except ValueError: description = f', description="{value}"' value = "1" if name not in already_seen: output.append(f"# HELP {name} {help}") already_seen.append(name) output.append(f'{name}{{forecast="{for_day}"{description}}} {value}') return "\n".join(output) + "\n" def _convert_time_to_minutes(time_str): "Convert time from midnight to minutes" return ( int( ( datetime.strptime(time_str, "%I:%M %p") - datetime.strptime("12:00 AM", "%I:%M %p") ).total_seconds() ) // 60 ) def render_prometheus(data): """ Convert `data` into Prometheus format and return it as string. """ already_seen = [] answer = _render_current(data["current_condition"][0], already_seen=already_seen) for i in range(3): answer += _render_current( data["weather"][i], for_day="%sd" % i, already_seen=already_seen ) return answer ================================================ FILE: lib/view/v2.py ================================================ # vim: fileencoding=utf-8 # vim: foldmethod=marker foldenable: """ [X] emoji [ ] wego icon [ ] v2.wttr.in [X] astronomical (sunset) [X] time [X] frames [X] colorize rain data [ ] date + locales [X] wind color [ ] highlight current date [ ] bind to real site [ ] max values: temperature [X] max value: rain [ ] comment github [ ] commit """ import sys import re import math import json import datetime import io import requests import diagram import pyjq import pytz import numpy as np from astral import LocationInfo from astral import moon, sun from scipy.interpolate import interp1d from babel.dates import format_datetime from globals import WWO_KEY, TRANSLATION_TABLE, remove_ansi import constants import translations import parse_query from . import line as wttr_line if not sys.version_info >= (3, 0): reload(sys) # noqa: F821 sys.setdefaultencoding("utf-8") # data processing {{{ def get_data(config): """ Fetch data for `query_string` """ url = ( "http://" "localhost:5001/premium/v1/weather.ashx" "?key=%s" "&q=%s&format=json&num_of_days=3&tp=3&lang=None" ) % (WWO_KEY, config["location"]) text = requests.get(url).text parsed_data = json.loads(text) return parsed_data def interpolate_data(input_data, max_width): """ Resample `input_data` to number of `max_width` counts """ input_data = list(input_data) input_data_len = len(input_data) x = list(range(input_data_len)) y = input_data xvals = np.linspace(0, input_data_len - 1, max_width) yinterp = interp1d(x, y, kind="cubic") return yinterp(xvals) def jq_query(query, data_parsed): """ Apply `query` to structued data `data_parsed` """ pyjq_data = pyjq.all(query, data_parsed) data = list(map(float, pyjq_data)) return data # }}} # utils {{{ def colorize(string, color_code, html_output=False): if html_output: return "%s" % (string) else: return "\033[%sm%s\033[0m" % (color_code, string) # }}} # draw_spark {{{ def draw_spark(data, height, width, color_data): """ Spark-style visualize `data` in a region `height` x `width` """ _BARS = " _▁▂▃▄▅▇█" def _box(height, row, value, max_value): row_height = 1.0 * max_value / height if row_height * row >= value: return _BARS[0] if row_height * (row + 1) <= value: return _BARS[-1] return _BARS[ int(1.0 * (value - row_height * row) / (row_height * 1.0) * len(_BARS)) ] max_value = max(data) output = "" color_code = 20 for i in range(height): for j in range(width): character = _box(height, height - i - 1, data[j], max_value) if data[j] != 0: chance_of_rain = color_data[j] / 100.0 * 2 if chance_of_rain > 1: chance_of_rain = 1 color_index = int(5 * chance_of_rain) color_code = ( 16 + color_index ) # int(math.floor((20-16) * 1.0 * (height-1-i)/height*(max_value/data[j]))) output += "\033[38;5;%sm%s\033[0m" % (color_code, character) output += "\n" # labeling max value if max_value == 0: max_line = " " * width else: max_line = "" for j in range(width): if data[j] == max_value: max_line = "%3.2fmm|%s%%" % (max_value, int(color_data[j])) orig_max_line = max_line # aligning it if len(max_line) // 2 < j and len(max_line) // 2 + j < width: spaces = " " * (j - len(max_line) // 2) max_line = spaces + max_line # + spaces max_line = max_line + " " * (width - len(max_line)) elif len(max_line) // 2 + j >= width: max_line = " " * (width - len(max_line)) + max_line max_line = max_line.replace( orig_max_line, colorize(orig_max_line, "38;5;33") ) break if max_line: output = "\n" + max_line + "\n" + output + "\n" return output # }}} # draw_diagram {{{ def draw_diagram(data, height, width): option = diagram.DOption() option.size = diagram.Point([width, height]) option.mode = "g" stream = io.BytesIO() gram = diagram.DGWrapper( data=[list(data), range(len(data))], dg_option=option, ostream=stream ) gram.show() return stream.getvalue().decode("utf-8") # }}} # draw_date {{{ def draw_date(config, geo_data): """ """ tzinfo = pytz.timezone(geo_data["timezone"]) locale = config.get("locale", "en_US") datetime_day_start = datetime.datetime.utcnow() answer = "" for day in range(3): datetime_ = datetime_day_start + datetime.timedelta(hours=24 * day) date = format_datetime(datetime_, "EEE dd MMM", locale=locale, tzinfo=tzinfo) spaces = ((24 - len(date)) // 2) * " " date = spaces + date + spaces date = " " * (24 - len(date)) + date answer += date answer += "\n" for _ in range(3): answer += " " * 23 + "╷" return answer[:-1] + " " # }}} # draw_time {{{ def draw_time(geo_data): """ """ tzinfo = pytz.timezone(geo_data["timezone"]) line = ["", ""] for _ in range(3): part = "─" * 5 + "┴" + "─" * 5 line[0] += part + "┼" + part + "╂" line[0] += "\n" for _ in range(3): line[1] += " 6 12 18 " line[1] += "\n" # highlight current time hour_number = ( datetime.datetime.now(tzinfo) - datetime.datetime.now(tzinfo).replace( hour=0, minute=0, second=0, microsecond=0 ) ).seconds // 3600 for line_number, _ in enumerate(line): line[line_number] = ( line[line_number][:hour_number] + colorize(line[line_number][hour_number], "46") + line[line_number][hour_number + 1 :] ) return "".join(line) # }}} # draw_astronomical {{{ def draw_astronomical(city_name, geo_data, config): datetime_day_start = datetime.datetime.now().replace( hour=0, minute=0, second=0, microsecond=0 ) city = LocationInfo() city.latitude = geo_data["latitude"] city.longitude = geo_data["longitude"] city.timezone = geo_data["timezone"] answer = "" moon_line = "" for time_interval in range(72): current_date = ( datetime_day_start + datetime.timedelta(hours=1 * time_interval) ).replace(tzinfo=pytz.timezone(geo_data["timezone"])) try: dawn = sun.dawn(city.observer, date=current_date) except ValueError: dawn = current_date try: dusk = sun.dusk(city.observer, date=current_date) except ValueError: dusk = current_date + datetime.timedelta(hours=24) try: sunrise = sun.sunrise(city.observer, date=current_date) except ValueError: sunrise = current_date try: sunset = sun.sunset(city.observer, date=current_date) except ValueError: sunset = current_date + datetime.timedelta(hours=24) char = "." if current_date < dawn: char = " " elif current_date > dusk: char = " " elif dawn <= current_date and current_date <= sunrise: char = "─" elif sunset <= current_date and current_date <= dusk: char = "─" elif sunrise <= current_date and current_date <= sunset: char = "━" answer += char if config.get("view") in ["v2n", "v2d"]: moon_phases = constants.MOON_PHASES_WI moon_phases = [" %s" % x for x in moon_phases] else: moon_phases = constants.MOON_PHASES # moon if time_interval in [0, 23, 47, 69]: # time_interval % 3 == 0: moon_phase = moon.phase( date=datetime_day_start + datetime.timedelta(hours=time_interval) ) moon_phase_emoji = moon_phases[ int(math.floor(moon_phase * 1.0 / 28.0 * 8 + 0.5)) % len(moon_phases) ] # if time_interval in [0, 24, 48, 69]: moon_line += moon_phase_emoji # + " " elif time_interval % 3 == 0: if time_interval not in [24, 28]: # se: moon_line += " " else: moon_line += " " answer = moon_line + "\n" + answer + "\n" answer += "\n" return answer # }}} # draw_emoji {{{ def draw_emoji(data, config): answer = "" if config.get("view") == "v2n": weather_symbol = constants.WEATHER_SYMBOL_WI_NIGHT weather_symbol_width_vte = constants.WEATHER_SYMBOL_WIDTH_VTE_WI elif config.get("view") == "v2d": weather_symbol = constants.WEATHER_SYMBOL_WI_DAY weather_symbol_width_vte = constants.WEATHER_SYMBOL_WIDTH_VTE_WI else: weather_symbol = constants.WEATHER_SYMBOL weather_symbol_width_vte = constants.WEATHER_SYMBOL_WIDTH_VTE for i in data: emoji = weather_symbol.get(constants.WWO_CODE.get(str(int(i)), "Unknown")) space = " " * (3 - weather_symbol_width_vte.get(emoji, 1)) answer += space[:1] + emoji + space[1:] answer += "\n" return answer # }}} # draw_wind {{{ def draw_wind(data, color_data, config): def _color_code_for_wind_speed(wind_speed): color_codes = [ (3, 241), # 82 (6, 242), # 118 (9, 243), # 154 (12, 246), # 190 (15, 250), # 226 (19, 253), # 220 (23, 214), (27, 208), (31, 202), (-1, 196), ] for this_wind_speed, this_color_code in color_codes: if wind_speed <= this_wind_speed: return this_color_code return color_codes[-1][1] answer = "" answer_line2 = "" if config.get("view") in ["v2n", "v2d"]: wind_direction_list = constants.WIND_DIRECTION_WI else: wind_direction_list = constants.WIND_DIRECTION for j, degree in enumerate(data): degree = int(degree) if degree: wind_direction = wind_direction_list[int(((degree + 22.5) % 360) / 45.0)] else: wind_direction = "" color_code = "38;5;%s" % _color_code_for_wind_speed(int(color_data[j])) answer += " %s " % colorize(wind_direction, color_code) # wind_speed wind_speed = int(color_data[j]) wind_speed_str = colorize(str(wind_speed), color_code) if wind_speed < 10: wind_speed_str = " " + wind_speed_str + " " elif wind_speed < 100: wind_speed_str = " " + wind_speed_str answer_line2 += wind_speed_str answer += "\n" answer += answer_line2 + "\n" return answer # }}} # panel implementation {{{ def add_frame(output, width, config): """ Add frame arond `output` that has width `width` """ empty_line = " " * width output = ( "\n".join("│" + (x or empty_line) + "│" for x in output.splitlines()) + "\n" ) weather_report = ( translations.CAPTION[config.get("lang") or "en"] + " " + (config["override_location_name"] or config["location"]) ) caption = "┤ " + " " + weather_report + " " + " ├" output = ( "┌" + caption + "─" * (width - len(caption)) + "┐\n" + output + "└" + "─" * width + "┘\n" ) return output def generate_panel(data_parsed, geo_data, config): """ """ max_width = 72 if config.get("use_imperial"): feels_like_query = "[.data.weather[] | .hourly[]] | .[].FeelsLikeF" temp_query = "[.data.weather[] | .hourly[]] | .[].tempF" wind_speed_query = "[.data.weather[] | .hourly[]] | .[].windspeedMiles" else: feels_like_query = "[.data.weather[] | .hourly[]] | .[].FeelsLikeC" temp_query = "[.data.weather[] | .hourly[]] | .[].tempC" wind_speed_query = "[.data.weather[] | .hourly[]] | .[].windspeedKmph" precip_mm_query = "[.data.weather[] | .hourly[]] | .[].precipMM" precip_chance_query = "[.data.weather[] | .hourly[]] | .[].chanceofrain" weather_code_query = "[.data.weather[] | .hourly[]] | .[].weatherCode" wind_direction_query = "[.data.weather[] | .hourly[]] | .[].winddirDegree" output = "" output += "\n\n" output += draw_date(config, geo_data) output += "\n" output += "\n" output += "\n" # data = jq_query(feels_like_query, data_parsed) data = jq_query(temp_query, data_parsed) data_interpolated = interpolate_data(data, max_width) output += draw_diagram(data_interpolated, 10, max_width) output += "\n" output += draw_time(geo_data) data = jq_query(precip_mm_query, data_parsed) color_data = jq_query(precip_chance_query, data_parsed) data_interpolated = interpolate_data(data, max_width) color_data_interpolated = interpolate_data(color_data, max_width) output += draw_spark(data_interpolated, 5, max_width, color_data_interpolated) output += "\n" data = jq_query(weather_code_query, data_parsed) output += draw_emoji(data, config) data = jq_query(wind_direction_query, data_parsed) color_data = jq_query(wind_speed_query, data_parsed) output += draw_wind(data, color_data, config) output += "\n" output += draw_astronomical(config["location"], geo_data, config) output += "\n" output = add_frame(output, max_width, config) return output # }}} # textual information {{{ def textual_information(data_parsed, geo_data, config, html_output=False): """ Add textual information about current weather and astronomical conditions """ def _shorten_full_location(full_location, city_only=False): def _count_runes(string): return len(string.encode("utf-16-le")) // 2 words = full_location.split(",") output = words[0] if city_only: return output for word in words[1:]: if _count_runes(output + "," + word) > 50: return output output += "," + word return output def _colorize(text, color): return colorize(text, color, html_output=html_output) city = LocationInfo() city.latitude = geo_data["latitude"] city.longitude = geo_data["longitude"] city.timezone = geo_data["timezone"] output = [] timezone = city.timezone datetime_day_start = datetime.datetime.now().replace( hour=0, minute=0, second=0, microsecond=0 ) format_line = "%c %C, %t, %h, %w, %P" current_condition = data_parsed["data"]["current_condition"][0] query = config weather_line = wttr_line.render_line(format_line, current_condition, query) output.append("Weather: %s" % weather_line) output.append("Timezone: %s" % timezone) local_tz = pytz.timezone(timezone) def _get_local_time_of(what): _sun = { "dawn": sun.dawn, "sunrise": sun.sunrise, "noon": sun.noon, "sunset": sun.sunset, "dusk": sun.dusk, }[what] current_time_of_what = _sun(city.observer, date=datetime_day_start) return ( current_time_of_what.replace(tzinfo=pytz.utc) .astimezone(local_tz) .strftime("%H:%M:%S") ) local_time_of = {} for what in ["dawn", "sunrise", "noon", "sunset", "dusk"]: try: local_time_of[what] = _get_local_time_of(what) except ValueError: local_time_of[what] = "-" * 8 tmp_output = [] tmp_output.append(" Now: %%{{NOW(%s)}}" % timezone) tmp_output.append("Dawn: %s" % local_time_of["dawn"]) tmp_output.append("Sunrise: %s" % local_time_of["sunrise"]) tmp_output.append(" Zenith: %s " % local_time_of["noon"]) tmp_output.append("Sunset: %s" % local_time_of["sunset"]) tmp_output.append("Dusk: %s" % local_time_of["dusk"]) tmp_output = [ re.sub("^([A-Za-z]*:)", lambda m: _colorize(m.group(1), "2"), x) for x in tmp_output ] output.append( "%20s" % tmp_output[0] + " | %20s " % tmp_output[1] + " | %20s" % tmp_output[2] ) output.append( "%20s" % tmp_output[3] + " | %20s " % tmp_output[4] + " | %20s" % tmp_output[5] ) city_only = False suffix = "" if "Simferopol" in timezone: city_only = True suffix = ", Крым" latitude = float(geo_data["latitude"]) longitude = float(geo_data["longitude"]) if config["full_address"]: output.append( "Location: %s%s [%5.4f,%5.4f]" % ( _shorten_full_location(config["full_address"], city_only=city_only), suffix, latitude, longitude, ) ) output = [ re.sub( "^( *[A-Za-z]*:)", lambda m: _colorize(m.group(1), "2"), re.sub( "^( +[A-Za-z]*:)", lambda m: _colorize(m.group(1), "2"), re.sub(r"(\|)", lambda m: _colorize(m.group(1), "2"), x), ), ) for x in output ] return "".join("%s\n" % x for x in output) # }}} # get_geodata {{{ def get_geodata(location): text = requests.get( "http://127.0.0.1:8085/:geo-location?location=%s" % location ).text return json.loads(text) # }}} def main(query, parsed_query, data): parsed_query["locale"] = "en_US" location = parsed_query["location"] html_output = parsed_query["html_output"] geo_data = get_geodata(location) if data is None: data_parsed = get_data(parsed_query) else: data_parsed = data if html_output: parsed_query["text"] = "no" filename = "b_" + parse_query.serialize(parsed_query) + ".png" output = """ Weather report for {orig_location}
{textual_information}
""".format( filename=filename, orig_location=parsed_query["orig_location"], textual_information=textual_information( data_parsed, geo_data, parsed_query, html_output=True ), ) else: output = generate_panel(data_parsed, geo_data, parsed_query) if query.get("text") != "no" and parsed_query.get("text") != "no": output += textual_information(data_parsed, geo_data, parsed_query) if parsed_query.get("no-terminal", False): output = remove_ansi(output) if parsed_query.get("dumb", False): output = output.translate(TRANSLATION_TABLE) return output if __name__ == "__main__": sys.stdout.write(main(sys.argv[1])) ================================================ FILE: lib/view/wttr.py ================================================ # vim: set encoding=utf-8 # pylint: disable=wrong-import-position """ Main view (wttr.in) implementation. The module is a wrapper for the modified Wego program. """ import sys import re from gevent.subprocess import Popen, PIPE sys.path.insert(0, "..") from translations import get_message, SUPPORTED_LANGS from globals import ( WEGO, TRANSLATION_TABLE, NOT_FOUND_LOCATION, DEFAULT_LOCATION, ANSI2HTML, error, remove_ansi, ) def get_wetter(parsed_query): location = parsed_query["location"] html = parsed_query["html_output"] lang = parsed_query["lang"] location_not_found = False if location == NOT_FOUND_LOCATION: location_not_found = True stderr = "" returncode = 0 if not location_not_found: stdout, stderr, returncode = _wego_wrapper(location, parsed_query) first_line, stdout = _wego_postprocessing(location, parsed_query, stdout) if location_not_found or ( returncode != 0 and ( "Unable to find any matching weather" " location to the parsed_query submitted" ) in stderr ): stdout, stderr, returncode = _wego_wrapper(DEFAULT_LOCATION, parsed_query) location_not_found = True not_found_header = """ >>> _ _ ___ _ _ >>> | || | / _ \| || | >>> | || |_| | | | || |_ >>> |__ _| |_| |__ _| >>> |_| \___/ |_| >>> >>> 404 %s: %s >>> """ % ( get_message("UNKNOWN_LOCATION", lang).upper(), parsed_query["override_location_name"], ) not_found_header = "\n".join( "\033[48;5;91m" + x + " \033[0m" for x in not_found_header.splitlines()[1:] ) not_found_footer = get_message("NOT_FOUND_MESSAGE", lang) not_found_footer = ( "\n".join( "\033[48;5;91m " + x + " \033[0m" for x in not_found_footer.splitlines() if x ) + "\n" ) first_line, stdout = _wego_postprocessing(location, parsed_query, stdout) stdout = not_found_header + "\n----\n" + stdout + not_found_footer if html: return _htmlize(stdout, first_line, parsed_query) return stdout def _wego_wrapper(location, parsed_query): lang = parsed_query["lang"] if location == DEFAULT_LOCATION: location_name = DEFAULT_LOCATION.capitalize() else: location_name = parsed_query["override_location_name"] cmd = [WEGO, "--city=%s" % location] if parsed_query.get("inverted_colors"): cmd += ["-inverse"] if parsed_query.get("use_ms_for_wind"): cmd += ["-wind_in_ms"] if parsed_query.get("narrow"): cmd += ["-narrow"] if lang and lang in SUPPORTED_LANGS: cmd += ["-lang=%s" % lang] if parsed_query.get("use_imperial", False): cmd += ["-imperial"] if location_name: cmd += ["-location_name", location_name] proc = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout, stderr = proc.communicate() stdout = stdout.decode("utf-8") stderr = stderr.decode("utf-8") return stdout, stderr, proc.returncode def _wego_postprocessing(location, parsed_query, stdout): full_address = parsed_query["full_address"] lang = parsed_query["lang"] if "days" in parsed_query: if parsed_query["days"] == "0": stdout = "\n".join(stdout.splitlines()[:7]) + "\n" if parsed_query["days"] == "1": stdout = "\n".join(stdout.splitlines()[:17]) + "\n" if parsed_query["days"] == "2": stdout = "\n".join(stdout.splitlines()[:27]) + "\n" lines = stdout.splitlines() if not lines: lines = [""] first = lines[0] rest = lines[1:] if parsed_query.get("no-caption", False): if ":" in first: first = first.split(":", 1)[1] stdout = "\n".join([first.strip()] + rest) + "\n" if parsed_query.get("no-terminal", False): stdout = remove_ansi(stdout) if parsed_query.get("no-city", False): stdout = "\n".join(stdout.splitlines()[2:]) + "\n" if parsed_query.get("dumb", False): stdout = stdout.translate(TRANSLATION_TABLE) if ( full_address and parsed_query.get("format", "txt") != "png" and ( not parsed_query.get("no-city") and not parsed_query.get("no-caption") and not parsed_query.get("days") == "0" ) ): line = "%s: %s [%s]\n" % (get_message("LOCATION", lang), full_address, location) stdout += line if parsed_query.get("padding", False): lines = [x.rstrip() for x in stdout.splitlines()] max_l = max(len(remove_ansi(x)) for x in lines) last_line = " " * max_l + " .\n" stdout = " \n" + "\n".join(" %s " % x for x in lines) + "\n" + last_line return first, stdout def _htmlize(ansi_output, title, parsed_query): """Return HTML representation of `ansi_output`. Use `title` as the title of the page. Format page according to query parameters from `parsed_query`.""" cmd = ["bash", ANSI2HTML, "--palette=solarized"] if not parsed_query.get("inverted_colors"): cmd += ["--bg=dark"] proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) stdout, stderr = proc.communicate(ansi_output.encode("utf-8")) stdout = stdout.decode("utf-8") stderr = stderr.decode("utf-8") if proc.returncode != 0: error(stdout + stderr) if parsed_query.get("inverted_colors"): stdout = stdout.replace( '', '' ) title = "%s" % title opengraph = _get_opengraph(parsed_query) stdout = re.sub("", "" + title + opengraph, stdout) return stdout def _get_opengraph(parsed_query): """Return OpenGraph data for `parsed_query`""" url = parsed_query["request_url"] or "" pic_url = url.replace("?", "_") return ( '' '' '' '' ) % { "pic_url": pic_url, "url": url, } ================================================ FILE: lib/weather_data.py ================================================ """ Weather data source """ import json import requests from globals import WWO_KEY def get_weather_data(location, lang): """ Get weather data for `location` """ key = WWO_KEY url = ( "/premium/v1/weather.ashx" "?key=%s&q=%s&format=json" "&num_of_days=3&tp=3&lang=%s" ) % (key, location, lang) url = "http://127.0.0.1:5001" + url response = requests.get(url, timeout=10) try: data = json.loads(response.content) except ValueError: data = {} return data ================================================ FILE: lib/wttr_srv.py ================================================ #!/usr/bin/env python # vim: set encoding=utf-8 """ Main wttr.in rendering function implementation """ import logging import io import os import time from gevent.threadpool import ThreadPool from flask import render_template, send_file, make_response import fmt.png import parse_query from translations import ( get_message, FULL_TRANSLATION, PARTIAL_TRANSLATION, SUPPORTED_LANGS, ) from buttons import add_buttons from globals import ( get_help_file, remove_ansi, TRANSLATION_TABLE, BASH_FUNCTION_FILE, TRANSLATION_FILE, LOG_FILE, NOT_FOUND_LOCATION, MALFORMED_RESPONSE_HTML_PAGE, PLAIN_TEXT_AGENTS, PLAIN_TEXT_PAGES, MY_EXTERNAL_IP, QUERY_LIMITS, ) from location import is_location_blocked, location_processing from limits import Limits from view.wttr import get_wetter from view.moon import get_moon from view.line import wttr_line import cache if not os.path.exists(os.path.dirname(LOG_FILE)): os.makedirs(os.path.dirname(LOG_FILE)) logging.basicConfig( filename=LOG_FILE, level=logging.INFO, format="%(asctime)s %(message)s" ) LIMITS = Limits(whitelist=[MY_EXTERNAL_IP], limits=QUERY_LIMITS) TASKS = ThreadPool(25) def show_text_file(name, lang): """ show static file `name` for `lang` """ text = "" if name == ":help": text = open(get_help_file(lang), "r").read() text = text.replace("FULL_TRANSLATION", " ".join(FULL_TRANSLATION)) text = text.replace("PARTIAL_TRANSLATION", " ".join(PARTIAL_TRANSLATION)) elif name == ":bash.function": text = open(BASH_FUNCTION_FILE, "r").read() elif name == ":iterm2": text = open("share/iterm2.txt", "r").read() elif name == ":translation": text = open(TRANSLATION_FILE, "r").read() text = text.replace("NUMBER_OF_LANGUAGES", str(len(SUPPORTED_LANGS))).replace( "SUPPORTED_LANGUAGES", " ".join(SUPPORTED_LANGS) ) return text def _client_ip_address(request): """Return client ip address for flask `request`.""" if request.headers.getlist("X-PNG-Query-For"): ip_addr = request.headers.getlist("X-PNG-Query-For")[0] if ip_addr.startswith("::ffff:"): ip_addr = ip_addr[7:] elif request.headers.getlist("X-Forwarded-For"): ip_addr = request.headers.getlist("X-Forwarded-For")[0] if ip_addr.startswith("::ffff:"): ip_addr = ip_addr[7:] else: ip_addr = request.remote_addr return ip_addr def _parse_language_header(header): """ >>> _parse_language_header("en-US,en;q=0.9") >>> _parse_language_header("en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7") >>> _parse_language_header("xx, fr-CA;q=0.8, da-DK;q=0.9") 'da' """ def _parse_accept_language(accept_language): languages = accept_language.split(",") locale_q_pairs = [] for language in languages: try: if language.split(";")[0] == language: # no q => q = 1 locale_q_pairs.append((language.strip(), 1)) else: locale = language.split(";")[0].strip() weight = float(language.split(";")[1].split("=")[1]) locale_q_pairs.append((locale, weight)) except (IndexError, ValueError): pass return locale_q_pairs def _find_supported_language(accepted_languages): def supported_langs(): """Yields all pairs in the Accept-Language header supported in SUPPORTED_LANGS or None if 'en' is the preferred""" for lang_tuple in accepted_languages: lang = lang_tuple[0] if "-" in lang: lang = lang.split("-", 1)[0] if lang in SUPPORTED_LANGS: yield lang, lang_tuple[1] elif lang == "en": yield None, lang_tuple[1] try: return max(supported_langs(), key=lambda lang_tuple: lang_tuple[1])[0] except ValueError: return None return _find_supported_language(_parse_accept_language(header)) def get_answer_language_and_view(request): """ Return preferred answer language based on domain name, query arguments and headers """ lang = None view_name = None hostname = request.headers["Host"] if hostname != "wttr.in" and hostname.endswith(".wttr.in"): lang = hostname[:-8] if lang.startswith("v2") or lang.startswith("v3"): view_name = lang lang = None if "lang" in request.args: lang = request.args.get("lang") if lang.lower() == "none": lang = None header_accept_language = request.headers.get("Accept-Language", "") if lang is None and header_accept_language: lang = _parse_language_header(header_accept_language) return lang, view_name def get_output_format(query, parsed_query): """ Return preferred output format: ansi, text, html or png based on arguments and headers in `request`. Return new location (can be rewritten) """ if ( ( "view" in query and not query["view"].startswith("v2") and not query["view"].startswith("v3") ) or parsed_query.get("png_filename") or query.get("force-ansi") ): return False user_agent = parsed_query.get("user_agent", "").lower() html_output = not any(agent in user_agent for agent in PLAIN_TEXT_AGENTS) return html_output def _cyclic_location_selection(locations, period): """Return one of `locations` (: separated list) basing on the current time and query interval `period` """ locations = locations.split(":") max_len = max(len(x) for x in locations) locations = [x.rjust(max_len) for x in locations] try: period = int(period) except ValueError: period = 1 index = int(time.time() / period) % len(locations) return locations[index] def _response(parsed_query, query, fast_mode=False): """Create response text based on `parsed_query` and `query` data. If `fast_mode` is True, process only requests that can be handled very fast (cached and static files). """ answer = None cache_signature = cache.get_signature( parsed_query["user_agent"], parsed_query["request_url"], parsed_query["ip_addr"], parsed_query["lang"], ) answer = cache.get(cache_signature) if parsed_query["orig_location"] in PLAIN_TEXT_PAGES: answer = show_text_file(parsed_query["orig_location"], parsed_query["lang"]) if parsed_query["html_output"]: answer = render_template("index.html", body=answer) if answer or fast_mode: return answer # at this point, we could not handle the query fast, # so we handle it with all available logic loc = (parsed_query["orig_location"] or "").lower() if parsed_query.get("view"): if not parsed_query.get("location"): parsed_query["location"] = loc output = wttr_line(query, parsed_query) elif loc == "moon" or loc.startswith("moon@"): output = get_moon(parsed_query) else: output = get_wetter(parsed_query) if parsed_query.get("png_filename"): if parsed_query.get("view") != "v3": # originally it was just a usual function call, # but it was a blocking call, so it was moved # to separate threads: # # output = fmt.png.render_ansi( # output, options=parsed_query) result = TASKS.spawn( fmt.png.render_ansi, cache._update_answer(output), options=parsed_query ) output = result.get() else: if ( query.get("days", "3") != "0" and not query.get("no-follow-line") and ((parsed_query.get("view") or "v2")[:2] in ["v2", "v3"]) ): if parsed_query["html_output"]: output = add_buttons(output) else: message = get_message("FOLLOW_ME", parsed_query["lang"]) if parsed_query.get("no-terminal", False): message = remove_ansi(message) if parsed_query.get("dumb", False): message = message.translate(TRANSLATION_TABLE) output += "\n" + message + "\n" return cache.store(cache_signature, output) def parse_request(location, request, query, fast_mode=False): """Parse request and provided extended information for the query, including location data, language, output format, view, etc. Incoming data: `location` location name extracted from the query url `request.args` `request.headers` `request.remote_addr` `request.referrer` `request.query_string` `query` parsed command line arguments Parameters priorities (from low to high): * HTTP-header * Domain name * URL * Filename Return: dictionary with parsed parameters """ if location and location.startswith("b_"): result = parse_query.deserialize(location) result["request_url"] = request.url if result: return result png_filename = None if location is not None and location.lower().endswith(".png"): png_filename = location location = location[:-4] if location and ":" in location and location[0] != ":": location = _cyclic_location_selection(location, query.get("period", 1)) parsed_query = { "ip_addr": _client_ip_address(request), "user_agent": request.headers.get("User-Agent", "").lower(), "request_url": request.url, } if png_filename: parsed_query["png_filename"] = png_filename parsed_query.update(parse_query.parse_wttrin_png_name(png_filename)) lang, _view = get_answer_language_and_view(request) parsed_query["view"] = parsed_query.get("view", query.get("view", _view)) parsed_query["location"] = parsed_query.get("location", location) parsed_query["orig_location"] = parsed_query["location"] parsed_query["lang"] = parsed_query.get("lang", lang) parsed_query["html_output"] = get_output_format(query, parsed_query) parsed_query["json_output"] = (parsed_query.get("view", "") or "").startswith("j") if not fast_mode: # not png_filename and not fast_mode: ( location, override_location_name, full_address, country, query_source_location, hemisphere, ) = location_processing(parsed_query["location"], parsed_query["ip_addr"]) us_ip = ( query_source_location[2] in ["United States", "United States of America"] and "slack" not in parsed_query["user_agent"] ) query = parse_query.metric_or_imperial(query, lang, us_ip=us_ip) if country and location != NOT_FOUND_LOCATION: location = "%s,%s" % (location, country) parsed_query.update( { "location": location, "override_location_name": override_location_name, "full_address": full_address, "country": country, "query_source_location": query_source_location, "hemisphere": hemisphere, } ) parsed_query.update(query) return parsed_query def wttr(location, request): """Main rendering function, it processes incoming weather queries, and depending on the User-Agent string and other paramters of the query it returns output in HTML, ANSI or other format. """ def _wrap_response(response_text, html_output, json_output, png_filename=None): if not isinstance(response_text, str) and not isinstance(response_text, bytes): return response_text if png_filename: response = make_response( send_file( io.BytesIO(response_text), download_name=png_filename, mimetype="image/png", ) ) for key, value in { "Cache-Control": "no-cache, no-store, must-revalidate", "Pragma": "no-cache", "Expires": "0", }.items(): response.headers[key] = value else: response = make_response(response_text) if html_output: response.mimetype = "text/html" elif json_output: response.mimetype = "application/json" else: response.mimetype = "text/plain" return response if is_location_blocked(location): return ("", 403) # Forbidden try: LIMITS.check_ip(_client_ip_address(request)) except RuntimeError as exception: return (str(exception), 429) # Too many requests query = parse_query.parse_query(request.args) # first, we try to process the query as fast as possible # (using the cache and static files), # and only if "fast_mode" was unsuccessful, # use the full track parsed_query = parse_request(location, request, query, fast_mode=True) response = _response(parsed_query, query, fast_mode=True) ############################ # Service Down. if os.path.exists("/tmp/service-down"): # parsed_query = parse_request(location, request, query) if parsed_query["html_output"]: response = MALFORMED_RESPONSE_HTML_PAGE http_code = 500 # Internal Server Error else: response = get_message("CAPACITY_LIMIT_REACHED", parsed_query["lang"]) http_code = 503 # Service Unavailable # if exception is occured, we return not a png file but text if "png_filename" in parsed_query: del parsed_query["png_filename"] return ( _wrap_response( response, parsed_query["html_output"], parsed_query["json_output"], png_filename=parsed_query.get("png_filename"), ), http_code, ) ############################ http_code = 200 try: if not response: parsed_query = parse_request(location, request, query) response = _response(parsed_query, query) # if not response or (isinstance(response, str) and not response.strip()): # return RuntimeError("Empty answer") if parsed_query["location"] == NOT_FOUND_LOCATION: http_code = 404 # pylint: disable=broad-except except Exception: logging.error("Exception has occured", exc_info=1) if parsed_query["html_output"]: response = MALFORMED_RESPONSE_HTML_PAGE http_code = 500 # Internal Server Error else: response = get_message("CAPACITY_LIMIT_REACHED", parsed_query["lang"]) http_code = 503 # Service Unavailable # if exception is occured, we return not a png file but text if "png_filename" in parsed_query: del parsed_query["png_filename"] return ( _wrap_response( response, parsed_query["html_output"], parsed_query["json_output"], png_filename=parsed_query.get("png_filename"), ), http_code, ) if __name__ == "__main__": import doctest doctest.testmod() ================================================ FILE: requirements.txt ================================================ flask geoip2 geopy requests gevent dnspython pylint cyrtranslit astral>=2.0,<=2.2 timezonefinder pytz pyte python-dateutil>=2.5.0,<=2.8.1 diagram pyjq scipy numpy pillow babel pylru>=1.0.7,<=1.2.1 pysocks supervisor numba emoji>=1.6.0,<=1.7.0 grapheme pycountry aiohttp>=3.10.11 # not directly required, pinned by Snyk to avoid a vulnerability yq ================================================ FILE: share/aliases ================================================ MDW : MDW Chicago mdw : MDW Chicago Msk : Moscow Moskva : Moscow Moskau : Moscow Kyiv : Kiev,Ukraine Kiew : Kiev,Ukraine Kiev : Kiev,Ukraine Kijev : Kiev Kharkov : Kharkiv spb : Saint Petersburg Dnipro : Dnipropetrovsk st.petersburg: Saint Petersburg sanfrancisco: San Francisco san fransisco:San Francisco San+Francisco:San Francisco San-Francisco:San Francisco Dnipropetrovsk:Dnepropetrovsk Dnepr : Dnipropetrovsk sanjose : San Jose san josé : San Jose newyork : New York newy-yourk : New York new-york : New York Saint-Petersburg: Saint Petersburg Krakow : Krakow,pl indonesia : Jakarta hanoi,vietnam:Hanoi nuernberg : Nuremberg nürnberg : Nuremberg norway : Oslo ville de bruxelles - stad brussel: Brussels frankfurt am main:Frankfurt, Hessen frankfurt :Frankfurt, Hessen frankfurt oder : Frankfurt, Brandenburg frankfurt (oder): Frankfurt, Brandenburg tel-aviv : Tel Aviv sao paulo : São Paulo los-angeles : Los Angeles Sevastopol : Sevastopol, Ukraine Simferopol : Simferopol, Ukraine Beersheva : Beersheba Be'ersheva : Beersheba Be'er Sheva : Beersheba Lugansk : Luhansk Bjalistoko : Białystok Chicago : Chicago,IL Paris : Paris,France Giessen : Giessen, Germany Braga : Braga, Portugal Kashan : ~Kashan,Iran Baku : Baku,Az Rome : Rome, Italia YYZ : Toronto Pearson Airport brasilia : Palacio da Alvorada,Brasilia Tula : Tula,Ru ================================================ FILE: share/ansi2html.sh ================================================ #!/bin/sh # Convert ANSI (terminal) colours and attributes to HTML # Licence: LGPLv2 # Author: # http://www.pixelbeat.org/docs/terminal_colours/ # Examples: # ls -l --color=always | ansi2html.sh > ls.html # git show --color | ansi2html.sh > last_change.html # Generally one can use the `script` util to capture full terminal output. # Changes: # V0.1, 24 Apr 2008, Initial release # V0.2, 01 Jan 2009, Phil Harnish # Support `git diff --color` output by # matching ANSI codes that specify only # bold or background colour. # P@draigBrady.com # Support `ls --color` output by stripping # redundant leading 0s from ANSI codes. # Support `grep --color=always` by stripping # unhandled ANSI codes (specifically ^[[K). # V0.3, 20 Mar 2009, http://eexpress.blog.ubuntu.org.cn/ # Remove cat -v usage which mangled non ascii input. # Cleanup regular expressions used. # Support other attributes like reverse, ... # P@draigBrady.com # Correctly nest tags (even across lines). # Add a command line option to use a dark background. # Strip more terminal control codes. # V0.4, 17 Sep 2009, P@draigBrady.com # Handle codes with combined attributes and color. # Handle isolated attributes with css. # Strip more terminal control codes. # V0.23, 22 Dec 2015 # http://github.com/pixelb/scripts/commits/master/scripts/ansi2html.sh gawk --version >/dev/null || exit 1 if [ "$1" = "--version" ]; then printf '0.22\n' && exit fi if [ "$1" = "--help" ]; then printf '%s\n' \ 'This utility converts ANSI codes in data passed to stdin It has 2 optional parameters: --bg=dark --palette=linux|solarized|tango|xterm E.g.: ls -l --color=always | ansi2html.sh --bg=dark > ls.html' >&2 exit fi [ "$1" = "--bg=dark" ] && { dark_bg=yes; shift; } if [ "$1" = "--palette=solarized" ]; then # See http://ethanschoonover.com/solarized P0=073642; P1=D30102; P2=859900; P3=B58900; P4=268BD2; P5=D33682; P6=2AA198; P7=EEE8D5; P8=002B36; P9=CB4B16; P10=586E75; P11=657B83; P12=839496; P13=6C71C4; P14=93A1A1; P15=FDF6E3; shift; elif [ "$1" = "--palette=solarized-xterm" ]; then # Above mapped onto the xterm 256 color palette P0=262626; P1=AF0000; P2=5F8700; P3=AF8700; P4=0087FF; P5=AF005F; P6=00AFAF; P7=E4E4E4; P8=1C1C1C; P9=D75F00; P10=585858; P11=626262; P12=808080; P13=5F5FAF; P14=8A8A8A; P15=FFFFD7; shift; elif [ "$1" = "--palette=tango" ]; then # Gnome default P0=000000; P1=CC0000; P2=4E9A06; P3=C4A000; P4=3465A4; P5=75507B; P6=06989A; P7=D3D7CF; P8=555753; P9=EF2929; P10=8AE234; P11=FCE94F; P12=729FCF; P13=AD7FA8; P14=34E2E2; P15=EEEEEC; shift; elif [ "$1" = "--palette=xterm" ]; then P0=000000; P1=CD0000; P2=00CD00; P3=CDCD00; P4=0000EE; P5=CD00CD; P6=00CDCD; P7=E5E5E5; P8=7F7F7F; P9=FF0000; P10=00FF00; P11=FFFF00; P12=5C5CFF; P13=FF00FF; P14=00FFFF; P15=FFFFFF; shift; else # linux console P0=000000; P1=AA0000; P2=00AA00; P3=AA5500; P4=0000AA; P5=AA00AA; P6=00AAAA; P7=AAAAAA; P8=555555; P9=FF5555; P10=55FF55; P11=FFFF55; P12=5555FF; P13=FF55FF; P14=55FFFF; P15=FFFFFF; [ "$1" = "--palette=linux" ] && shift fi [ "$1" = "--bg=dark" ] && { dark_bg=yes; shift; } # Mac OSX's GNU sed is installed as gsed # use e.g. homebrew 'gnu-sed' to get it if ! sed --version >/dev/null 2>&1; then if gsed --version >/dev/null 2>&1; then alias sed=gsed else echo "Error, can't find an acceptable GNU sed." >&2 exit 1 fi fi printf '%s' "
'

p='\x1b\['        #shortcut to match escape codes

# Handle various xterm control sequences.
# See /usr/share/doc/xterm-*/ctlseqs.txt
sed "
# escape ampersand and quote
s#&#\&#g; s#\"#\"#g;
s#\x1b[^\x1b]*\x1b\\\##g  # strip anything between \e and ST
s#\x1b][0-9]*;[^\a]*\a##g # strip any OSC (xterm title etc.)

s#\r\$## # strip trailing \r

# strip other non SGR escape sequences
s#[\x07]##g
s#\x1b[]>=\][0-9;]*##g
s#\x1bP+.\{5\}##g
# Mark cursor positioning codes \"Jr;c;
s#${p}\([0-9]\{1,2\}\)G#\"J;\1;#g
s#${p}\([0-9]\{1,2\}\);\([0-9]\{1,2\}\)H#\"J\1;\2;#g

# Mark clear as \"Cn where n=1 is screen and n=0 is to end-of-line
s#${p}H#\"C1;#g
s#${p}K#\"C0;#g
# Mark Cursor move columns as \"Mn where n is +ve for right, -ve for left
s#${p}C#\"M1;#g
s#${p}\([0-9]\{1,\}\)C#\"M\1;#g
s#${p}\([0-9]\{1,\}\)D#\"M-\1;#g
s#${p}\([0-9]\{1,\}\)P#\"X\1;#g

s#${p}[0-9;?]*[^0-9;?m]##g

" |

# Normalize the input before transformation
sed "
# escape HTML (ampersand and quote done above)
s#>#\>#g; s#<#\<#g;

# normalize SGR codes a little

# split 256 colors out and mark so that they're not
# recognised by the following 'split combined' line
:e
s#${p}\([0-9;]\{1,\}\);\([34]8;5;[0-9]\{1,3\}\)m#${p}\1m${p}¬\2m#g; t e
s#${p}\([34]8;5;[0-9]\{1,3\}\)m#${p}¬\1m#g;

:c
s#${p}\([0-9]\{1,\}\);\([0-9;]\{1,\}\)m#${p}\1m${p}\2m#g; t c   # split combined
s#${p}0\([0-7]\)#${p}\1#g                                 #strip leading 0
s#${p}1m\(\(${p}[4579]m\)*\)#\1${p}1m#g                   #bold last (with clr)
s#${p}m#${p}0m#g                                          #add leading 0 to norm

# undo any 256 color marking
s#${p}¬\([34]8;5;[0-9]\{1,3\}\)m#${p}\1m#g;

# map 16 color codes to color + bold
s#${p}9\([0-7]\)m#${p}3\1m${p}1m#g;
s#${p}10\([0-7]\)m#${p}4\1m${p}1m#g;

# change 'reset' code to \"R
s#${p}0m#\"R;#g
" |

# Convert SGR sequences to HTML
sed "
# common combinations to minimise html (optional)
:f
s#${p}3[0-7]m${p}3\([0-7]\)m#${p}3\1m#g; t f
:b
s#${p}4[0-7]m${p}4\([0-7]\)m#${p}4\1m#g; t b
s#${p}3\([0-7]\)m${p}4\([0-7]\)m##g
s#${p}4\([0-7]\)m${p}3\([0-7]\)m##g

s#${p}1m##g
s#${p}4m##g
s#${p}5m##g
s#${p}7m##g
s#${p}9m##g
s#${p}3\([0-9]\)m##g
s#${p}4\([0-9]\)m##g

s#${p}38;5;\([0-9]\{1,3\}\)m##g
s#${p}48;5;\([0-9]\{1,3\}\)m##g

s#${p}[0-9;]*m##g # strip unhandled codes
" |

# Convert alternative character set and handle cursor movement codes
# Note we convert here, as if we do at start we have to worry about avoiding
# conversion of SGR codes etc., whereas doing here we only have to
# avoid conversions of stuff between &...; or <...>
#
# Note we could use sed to do this based around:
#   sed 'y/abcdefghijklmnopqrstuvwxyz{}`~/▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π£◆·/'
# However that would be very awkward as we need to only conv some input.
# The basic scheme that we do in the awk script below is:
#  1. enable transliterate once "T1; is seen
#  2. disable once "T0; is seen (may be on diff line)
#  3. never transliterate between &; or <> chars
#  4. track x,y movements and active display mode at each position
#  5. buffer line/screen and dump when required
sed "
# change 'smacs' and 'rmacs' to \"T1 and \"T0 to simplify matching.
s#\x1b(0#\"T1;#g;
s#\x0E#\"T1;#g;

s#\x1b(B#\"T0;#g
s#\x0F#\"T0;#g
" |
(
gawk '
function dump_line(l,del,c,blanks,ret) {
  for(c=1;c")
  for(i=1;i<=spc;i++) {
    rm=rm?rm:(a[i]!=attr[i]">")
    if(rm) {
      ret=ret ""
      delete a[i];
    }
  }
  for(i=1;i"
    if(a[i]!=attr[i]) {
      a[i]=attr[i]
      ret = ret attr[i]
    }
  }
  return ret
}

function encode(string,start,end,i,ret,pos,sc,buf) {
   if(!end) end=length(string);
   if(!start) start=1;
   state=3
   for(i=1;i<=length(string);i++) {
     c=substr(string,i,1)
     if(state==2) {
       sc=sc c
       if(c==";") {
          c=sc
          state=last_mode
       } else continue
     } else {
       if(c=="\r") { x=1; continue }
       if(c=="<") {
         # Change attributes - store current active
         # attributes in span array
         split(substr(string,i),cord,">");
         i+=length(cord[1])
         span[++spc]=cord[1] ">"
         continue
       }
       else if(c=="&") {
         # All goes to single position till we see a semicolon
         sc=c
         state=2
         continue
       }
       else if(c=="\b") {
          # backspace move insertion point back 1
          if(spc) attr[x,y]=atos(span)
          x=x>1?x-1:1
          continue
       }
       else if(c=="\"") {
          split(substr(string,i+2),cord,";")
          cc=substr(string,i+1,1);
          if(cc=="T") {
              # Transliterate on/off
              if(cord[1]==1&&state==3) last_mode=state=4
              if(cord[1]==0&&state==4) last_mode=state=3
          }
          else if(cc=="C") {
              # Clear
              if(cord[1]+0) {
                # Screen - if Recording dump screen
                if(dumpStatus==dsActive) ret=ret dump_screen()
                dumpStatus=dsActive
                delete dump
                delete attr
                x=y=1
              } else {
                # To end of line
                for(pos=x;posmaxY) maxY=y
                # Change y - start recording
                dumpStatus=dumpStatus?dumpStatus:dsReset
              }
          }
          else if(cc=="M") {
              # Move left/right on current line
              x+=cord[1]
          }
          else if(cc=="X") {
              # delete on right
              for(pos=x;pos<=maxX;pos++) {
                nx=pos+cord[1]
                if(nx=start&&i<=end&&c in Trans) c=Trans[c]
     }
     if(dumpStatus==dsReset) {
       delete dump
       delete attr
       ret=ret"\n"
       dumpStatus=dsActive
     }
     if(dumpStatus==dsNew) {
       # After moving/clearing we are now ready to write
       # somthing to the screen so start recording now
       ret=ret"\n"
       dumpStatus=dsActive
     }
     if(dumpStatus==dsActive||dumpStatus==dsOff) {
       dump[x,y] = c
       if(!spc) delete attr[x,y]
       else attr[x,y] = atos(span)
       if(++x>maxX) maxX=x;
     }
    }
    # End of line if dumping increment y and set x back to first col
    x=1
    if(!dumpStatus) return ret dump_line(y,1);
    else if(++y>maxY) maxY=y;
    return ret
}
BEGIN{
  OFS=FS
  # dump screen status
  dsOff=0    # Not dumping screen contents just write output direct
  dsNew=1    # Just after move/clear waiting for activity to start recording
  dsReset=2  # Screen cleared build new empty buffer and record
  dsActive=3 # Currently recording
  F="abcdefghijklmnopqrstuvwxyz{}`~"
  T="▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π£◆·"
  maxX=80
  delete cur;
  x=y=1
  for(i=1;i<=length(F);i++)Trans[substr(F,i,1)]=substr(T,i,1);
}

{ $0=encode($0) }
1
END {
  if(dumpStatus) {
    print dump_screen();
  }
}'
)

printf '
\n' ================================================ FILE: share/bash-function.txt ================================================ #! /usr/bin/env bash # If you source this file, it will set WTTR_PARAMS as well as show weather. # WTTR_PARAMS is space-separated URL parameters, many of which are single characters that can be # lumped together. For example, "F q m" behaves the same as "Fqm". if [[ -z "$WTTR_PARAMS" ]]; then # Form localized URL parameters for curl if [[ -t 1 ]] && [[ "$(tput cols)" -lt 125 ]]; then WTTR_PARAMS+='n' fi 2> /dev/null for _token in $( locale LC_MEASUREMENT ); do case $_token in 1) WTTR_PARAMS+='m' ;; 2) WTTR_PARAMS+='u' ;; esac done 2> /dev/null unset _token export WTTR_PARAMS fi wttr() { local location="${1// /+}" test "$#" -gt 0 && shift local args=() for p in $WTTR_PARAMS "$@"; do args+=("--data-urlencode" "$p") done curl -fGsS -H "Accept-Language: ${LANG%_*}" "${args[@]}" --compressed "wttr.in/${location}" } wttr "$@" ================================================ FILE: share/blacklist ================================================ NOT_FOUND apple-touch-icon.png apple-touch-icon-precomposed.png apple-touch-icon-152x152-precomposed.png ================================================ FILE: share/docker/supervisord.conf ================================================ [supervisord] nodaemon=true logfile=/var/log/supervisor/supervisord.log pidfile=/var/run/supervisord.pid [program:srv] command=python3 /app/bin/srv.py stderr_logfile=/var/log/supervisor/srv-stderr.log stdout_logfile=/var/log/supervisor/srv-stdout.log [program:proxy] command=python3 /app/bin/proxy.py stderr_logfile=/var/log/supervisor/proxy-stderr.log stdout_logfile=/var/log/supervisor/proxy-stdout.log [program:geoproxy] command=python3 /app/bin/geo-proxy.py stderr_logfile=/var/log/supervisor/geoproxy-stderr.log stdout_logfile=/var/log/supervisor/geoproxy-stdout.log [include] files=/etc/supervisor/conf.d/*.conf ================================================ FILE: share/help.txt ================================================ Usage: $ curl wttr.in # current location $ curl wttr.in/muc # weather in the Munich airport Supported location types: /paris # city name /~Eiffel+tower # any location (+ for spaces) /Москва # Unicode name of any location in any language /muc # airport code (3 letters) /@stackoverflow.com # domain name /94107 # area codes /-78.46,106.79 # GPS coordinates Moon phase information: /moon # Moon phase (add ,+US or ,+France for these cities) /moon@2016-10-25 # Moon phase for the date (@2016-10-25) Units: m # metric (SI) (used by default everywhere except US) u # USCS (used by default in US) M # show wind speed in m/s View options: 0 # only current weather 1 # current weather + today's forecast 2 # current weather + today's + tomorrow's forecast A # ignore User-Agent and force ANSI output format (terminal) d # restrict output to standard console font glyphs F # do not show the "Follow" line n # narrow version (only day and night) q # quiet version (no "Weather report" text) Q # superquiet version (no "Weather report", no city name) T # switch terminal sequences off (no colors) PNG options: /paris.png # generate a PNG file p # add frame around the output t # transparency 150 transparency=... # transparency from 0 to 255 (255 = not transparent) background=... # background color in form RRGGBB, e.g. 00aaaa Options can be combined: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # in PNG the file mode are specified after _ /Rome_0pq_lang=it.png # long options are separated with underscore Localization: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Supported languages: FULL_TRANSLATION (supported) PARTIAL_TRANSLATION (in progress) Special URLs: /:help # show this page /:bash.function # show recommended bash function wttr() /:translation # show the information about the translators ================================================ FILE: share/iterm2.txt ================================================ ]1337;File=name=TnVyZW1iZXJnLnBuZw==;size=46226;inline=1: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 ================================================ FILE: share/list-of-iata-codes.txt ================================================ GKA MAG HGU LAE POM WWK UAK GOH SFJ THU AEY EGS HFN HZK IFJ KEF PFJ RKV SIJ VEY YAM YAV YAW YAY YAZ YBB YBC YBG YBK YBL YBR YCB YCD YCG YCH YCL YCO YCT YCW YCY YZS YDA YDB YDC YDF YDL YDN YDQ YEG YEK YEN YET YEU YEV YFB YFC YFO YFR YFS YGK YGL YGP YGQ YGR YHB YHD YHI YHK YHM YHU YHY YHZ YIB YIO YJN YJT YKA YKF YKL YKY YKZ YLD YLJ YLL YLT YLW YMA YMJ YMM YMO YMW YMX YNA YND YNM YOC YOD YOJ YOW YPA YPE YPG YPL YPN YPQ YPR YPY YQA YQB YQF YQG YQH YQK YQL YQM YQQ YQR YQT YQU YQV YQW YQX YQY YQZ YRB YRI YRJ YRM YRT YSB YSC YSJ YSM YSR YSU YSY YTE YTH YTR YTS YTZ YUB YUL YUT YUX YUY YVC YVG YVM YVO YVP YVQ YVR YVT YVV YWA YWG YWK YWL YWY YXC YXD YXE YXH YXJ YXL YXP YXR YXS YXT YXU YXX YXY YYB YYC YYD YYE YYF YYG YYH YYJ YYL YYN YYQ YYR YYT YYU YYW YYY YYZ YZD YZE YZF YZH YZP YZR YZT YZU YZV YZW YZX ZFA ZFM BJA ALG DJG QFD VVZ TMR GJL AAE CZL TEE HRM TID TIN QAS TAF TLM ORN MUW AZR BSK ELG GHA HME INZ TGR LOO TMX OGX IAM COO OUA BOY ACC TML NYI TKD ABJ BYK DJO HGO MJC SPY ASK ABV AKR BNI CBQ ENU QUS IBA ILR JOS KAD KAN MIU MDI LOS MXJ PHC SKO YOL ZAR MFQ NIM THZ AJY ZND MIR TUN GAF GAE DJE EBM SFA TOE LRL LFW ANR BRU CRL QKT LGG OST AOC BBH SXF DRS ERF FRA FMO HAM THF CGN DUS MUC NUE LEJ SCN STR TXL HAJ BRE QEF HHN MHG XFW KEL LBC ZCA ESS MGL PAD DTM AGB OBF RBM FDH SZW ZSN BYU HOQ ZNV ZQF ZQC ZQW ZQL BWE KSF BRV EME WVN BMK NRD FLF GWT KDL URE EPU TLL TAY ENF KEV HEM HEL HYV IVL JOE JYV KAU KEM KAJ KOK KAO KTT KUO LPP MHQ MIK OUL POR RVN SVL SOT TMP TKU QVY VAA VRK BFS ENK BHD LDY BHX CVT GLO MAN NQY LYE YEO CWL SWS BRS LPL LTN PLH BOH SOU QLA ACI GCI JER ESH BQH LGW LCY FAB BBS LHR SEN LYX MSE CAX BLK HUY BWF LBA CEG IOM NCL MME EMA KOI LSI WIC ABZ INV GLA EDI ILY PIK BEB SCS DND SYY TRE ADX LMO CBG NWI STN EXT FZO OXF MHZ FFD BZZ ODH NHT QCY BEQ HRT WTN KNF MPN AMS MST EIN GRQ DHR LEY LWR RTM UTC ENS LID WOE ORK GWY DUB NOC KIR SNN SXL WAT AAR BLL CPH EBJ KRP ODE RKE RNN SGD SKS TED FAE STA AAL LUX AES ANX ALF BNN BOO BGO BJF KRS BDU Norway VDB FRO OSL HAU HAA KSU KKN FAN MOL MJF LKL NTB OLA RRS RYG Norway SKE SRP SSJ TOS Norway TRD SVG GDN KRK KTW POZ RZE SZZ OSP WAW WRO IEG RNB GOT JKG LDK GSE KVB THN KSK MXX NYO KID JLD OSK KLR MMX HAD VXO EVG GEV HUV KRF LYC SDL OER KRN SFT UME VHM AJR ORB VST LLA ARN BMA BLE HLF GVX LPI NRK VBY SPM RMS GHF ZCN ZNF GKE RLG FEL IGS GUT ALJ AGZ BIY BFN CPT DUR ELS GCJ GRJ HDS JNB KIM KMH KLZ HLA LAY MGH MEZ NCS DUH PLZ PHW PZB NTY PTG UTW RCB ROD SBU SIS SZK LTA ULD UTN UTT VRU VIR PRY WEL FRW JWA BBK MUB GBE PKW BZV FTX OUE PNR MTS BGF BBT BSG SSG ASI MRU RRG TKC DLA MVR FOM NGE GOU BFX BPC YAO LVI LUN MFU NLA KIW HAH NWA AJN DZA RUN ZSE TNR ZVA SMS TMM MOQ DIE WMR ZWA AMB ANM HVA MJN NOS BPY WMN SVB VOH WAI IVA FTU WFI RVA WVK MNJ MXM TLE SSY BUG CAB NOV SVP LAD MEG SPP GXG PBN VHC SZA SDD LUO UGO XGN OYE OKN LBQ BMM POG OMB MKU LBV MVB PCP TMS BEW INH VXC MPM MZB MNC APL POL UEL TET VNX DES SEZ PRI AEH MQQ NDJ FYT BUQ BFO VFA HRE KAB MVZ GWE WKM BLZ KGJ LLW ZZU MSU FIH NLO MNB FDU KKW MDK BDT GMA LIQ FKI IRP BUX BKY GOM KND FBM KWZ FMI KMN KGA MJM BKO GAQ KYS MZI TOM BJL FUE VDE SPC LPA ACE TFS TFN MLN FNA MLW ROB AGA TTA FEZ ERH MEK OUD RBA CMN RAK NNA OZZ AHU TTU TNG ZIG CSK KLC DKR XLS BXE KGG TUD IEO TIY KFA EMN KED NKC SEY ATR NDB FIG FAA LEK SID BVC MMO SNE VXE ADD AMH AXU BJR DIR GMB GDQ JIM LLI MQX ASO BJM HGA BBO KMU ALY ABS CAI HRG EGR LXR MUH PSD SKV ASW ELT EDL KIS KTL LOK LAU MBA WIL WJR GHT AKF BEN SEB TIP LTD GYI KGL KME DOG ELF KSL EBD JUB MAK KRT ARK DAR DOD IRI JRO LKY MYW MWZ PMA TGT ZNZ EBB SRT TIA BOJ GOZ PDV SOF VAR LCA PFO AKT DBV OSI PUY RJK SPU ZAG ZAD ALC LEI OVD ODB BIO BCN BJZ LCG GRO GRX IBZ XRY MJV MAD AGP MAH OZP PNA REU SLM EAS SCQ LEU TOJ VLC VLL VIT VGO SDR ZAZ SVQ CQF LTQ AGF BOD EGC CNG PIS MCU LIG NIT TLS PUF LDE ANG BVE PGX BIQ XAC LBI DCM RDZ RYN RCO CMR DLE OBS LPY XBK XVF XMU ETZ BIA CLY FSC AJA SOZ AUF CMF CFE BOU XCD QNJ LYS QNX RNE NCY GNB VAF VHY AUR CHR LYN QXB CEQ EBU CCF MRS NCE PGF CTT MPL BZR AVN MEN BVA LEH ORE XCR URO TUF CET LVA LBG CSF CDG TNF ORY POX QYR NVS LIL BES CER DNR GFR DOL LRT EDM CFR LME RNS LAI UIP NTE SBK MXN VNE SNR BSL DIJ MZM EPL ENC RHE SXB TLN FNI MQC FSP PYR AGQ AXD VOL JKH IOA HER KSO KIT EFL KLX KGS AOK CFU KSJ KVA KZI LRS LXS LRA JMK MJT PVK RHO GPA CHQ JSI SMI JTR JSH SKU SKG ZTH BUD DEB CRV BRI FOG TAR LCC PSR BDS SUF CTA LMP PNL PMO REG TPS NSY AHO DCI CAG OLB TTB MXP BGY TRN ALL GOA LIN PMF QPZ CUF AVB BZO BLQ TSF FRL VBS TRS RMI VIC QPA VRN VCE SAY CIA FCO EBA QLT NAP PSA FLR GRS PEG LJU MBX POW KLV OSR PED PRV PRG BRQ TLV BEV ETH HFA RPN VDA SDV MLA GRZ INN LNZ SZG VIE SMA BGC FLW FAO GRW HOR TER PDL PIX OPO PXO LIS SJZ VRL OMO SJJ ARW BCM BAY BBU CND CLJ CSB CRA IAS OMR OTP SBZ SUJ SCV TCE TGM TSR GVA SIR EML LUG BRN ZRH ACH SMV ESB ANK ADA UAB AFY AYT GZT KYA MLX MZH VAS ASR DNZ IST BZI BDM ESK ADB IGL DLM BXN EZS DIY ERC ERZ TZX VAN BAL BZY KIV OHD SKP GIB BEG INI TGD PRN TIV BTS KSC PZY SLD TAT NCA PLS XSC EPS BRX LRM PUJ POP SDQ STI CBV GUA LCE SAP GJA RTB TEA TGU OCJ KIN MBJ POT KTP ACA NTR AGU HUX CVJ CME CUL CTM CEN CPE CJS CUU CVM CZM DGO TPQ ESE GDL GYM TCN HMO CLQ ISJ SLW LZC LMM BJX LAP LTO MAM MID MXL MLM MTT LOV MEX MTY MZT NOG NLD OAX PAZ PBC PCA PPE PDS UPN PVR PXM QRO REX SJD SFH SLP TXA TRC TGZ TIJ TAM TSL TLC TAP CUN VSA VER ZCL ZIH ZMM ZLO BEF MGA PUZ BOC CHX DAV BLB PAC PTY OTR GLF LIR LIO NOB SJO PMZ XQP SAL CAP PAP BCA BYM AVI CCC CFG CYO CMW SCU GAO HAV HOG LCL MOA MZO GER UPB SNU VRA VTU CYB GCM ASD MHH SAQ AXP TCB CCZ BIM GGT ELH GHB RSD FPO IGA LGI SML MYG NAS ZSA BZE AIT RAR NAN SUV TBU VAV TRW TBF WLS APW PPG RUR TUB AAA TIH REA FAV XMH GMR KKR MKP PKP TKP AXR MVT TKX NHV BOB RGI HUH MOZ HOI MAU RFP VLI KNQ KOC LIF GEA MEE TOU UVE NOU AKL TUO AMZ CHC CHT DUD GIS HKK HLZ KKE KAT ALR MON TEU MRO NPL NSN IVC OAM PMR PPQ ZQN ROT TRG TIU BHE WKA WHK WLG WRE WSZ WAG HEA JAA KBL KDH MMZ MZR UND BAH AHB HOF ABT BHH DMM DHA GIZ ELQ URY HAS JED HBT MED EAM AQI RAH RUH RAE SHW SLF TUU TIF TUI EJH YNB ABD QMJ MRX AWZ BUZ KIH BDH KSH SDG IFN RAS THR BND KER XBJ RZR SYZ TBZ AZD ACZ ZBR ZAH AMM ADJ AQJ OMF KWI BEY AUH AZI DXB FJR RKT SHJ KHS MSH MCT SLL TTH LYP GWD GIL KHI LHE MFG MJD MUX WNS PJG PSI PEW UET RYK ISB RAZ SKZ SDT SUL BDN PZH BSR ALP DAM DEZ LTK PMS CIS ROP SPN UAM GUM TIQ MAJ KWA CXI MDY TKK PNI ROR KSA YAP KNH TTT GNI KHH CYI KYD RMQ TNN MZG PIF TSA TPE WOT HUN NRT MMJ IBR IWO SHM OBO CTS HKD SPK MMB SHB WKJ IKI UBJ TSJ MBE AKJ OIR RIS KUM FUJ FUK TNE KOJ KMI OIT KKJ KMJ NGS ASJ TKN KMQ OKI TOY HIJ OKJ IZO YGJ KCZ MYJ ITM TTJ TKS TAK AOJ GAJ SDS HNA AXT MSJ SDJ HAC OIM HND OKO KWJ CHN RSU SHO KAG CJU PUS USN SSN OSN GMP SWU KPO TAE YEC OKA DNA ISG UEO MMD MMY KTD SHI RNJ OGN MNL CBO CGY PAG GES ZAM BAG SJI TAC BCD DGT MPH ILO KLO PPS COC GHU PRA ROS SFN AEP COR LPG MDZ LGS AFA CTC SDE IRJ TUC UAQ RCU VDR LUQ CNQ RES FMA IGR AOL PSS SLA JUJ ORA EHL CRD EQS REL VDM PMY PUD RGA RGL USH ULA PMQ RZA BHI MDQ NQN RSA BRC TDL VLG CPC CDJ AQA AJU AFL ARU BEL BGX PLU BFH BSB BAU BVB CAC CNF CGR XAP CLN CCM CAW CMG CWB CRQ CXJ CGB CZS PPB MAO IGU FLN FEN FOR GIG GYN GRU ATM ITB IOS IPN IMP JDF JPA JOI CPV VCP LIP LDB LAZ MAB MGF MOC PLL MCZ MCP MVF MNX NVT GEL NAT POA POO PFB PET PNZ PNB PMG PVH RBR REC SDU RAO STU SJK SLZ CGH SJP SSZ SSA TMT THE TFF TBT TUR SJL PAV URG UDI UBA VAG BVH VIX QPS ARI BBA CCH CJC PUQ GXQ IQQ SCL ANF WPR LSQ WPU CCP IPC ZOS LSC ZCO PMC WCH ZAL ATF OCC CUE GPS GYE LTX XMS MCH MEC PVO UIO ETR SNC TPC TUA ASU CIO AXM PUU BGA BOG BAQ BSC BUN CUC CTG CLO TCO CZU EJA FLA GPI IBE IPI APO LET EOH MGN MTR MVP MZL NVA OCV OTU PCR PEI PPN PSO PVA MDE RCH SJE SMR ADZ SVI TME AUC UIB ULQ VUP VVC BJO CBB CIJ LPB POI PSZ SRE TJA TDD VVI BYC PBM CAY PCL CHM CIX AYP ANS ATA LIM JJI JUL TBP YMS CHH IQT AQP TRU PIO TPP TCQ PEM PIU TYL CUZ MVD STY AGV AAO BLA BNS BRM CBL CAJ CUP CZE CUM GUI GUQ LSP LFR MAR MRD PMV CCS MUN PYH PBL PZO SVZ SNV STD SFD SOM STB TUV VLN VLV VDP LTM ANU BGI DCF DOM FDF SFG PTP GND STT STX BQN FAJ SIG MAZ PSE SJU SKB SLU UVF AUA BON CUR EUX SXM AXA TAB POS EIS CIW MQS SVD ALA BXH TSE DMB FRU OSS CIT URA PWQ PLX AKX GYD YKS MJZ BQS KHV PVS GDX PWE PKC UUS VVO HTA BTK IKT UUD KBP DOK DNK SIP IEV LWO ODS LED MMK GME VTB KGD MHP MSQ ABA BAX KEJ OMS KRR MCX MRV STW ROV AER ASF VOG CEK MQF NJC PEE SGC SVX ASB KRW CRZ DYU BHK SKD TAS BZK SVO KLD VOZ VKO SCW KZN REN UFA KUF AMD AKD IXU BOM PAB BHJ IXG BDQ BHO BHU NMB GOI IDR JLR JGA IXY HJR KLH IXK NAG ISK PNQ PBD RAJ RPR SSE STV UDR CMB ACJ RML GOY JAF TRR PNH REP TNX IXA AJL IXB BBI CCU COH DBD GAY IMF IXW JRH IXH IXS IXI DIB PAT IXR RRK VTZ CXB CGP IRD JSR RJH SPD ZYL DAC HKG AGR IXD ATQ VNS KUU IXC DED DEL GWL JDH JAI JSA IXJ KNU KTU LUH IXL LKO PGH SXR TNI LPQ PKZ ZVK VTE MFM BWA KTM PKR SIF BIR AGX BLR BEP VGA CJB COK CCJ CDP BPM IXM IXE MAA IXZ PNY RJA TIR TRZ TRV PBH MLE DMK UTP LPT PRH HHQ PHS NAW KBV PAN USM HKT UNN HDY TST UTH SNO LOE DAD HAN NHA HUI PQC SGN NYU HEH KET KYP LSH MDL MGZ MYT MOG PBU AKY SNW THL RGN UPG BIK NBX TIM DJJ WMX MKQ GTO PLW MDC PSJ OTI TTE LUW Indonesia FKQ KNG BXB MKW SOQ BTU KCH LMN MUR MYY SBW LDU BKI LBU TWU BWN PKU DUM CGK GNS MES KTG PNK DJB BKS PLM RGT BTJ AOR KBR KUA KTE IPH JHB KUL LGK MKZ TGG PEN DIL QPG XSP SIN ABM ASP BNE OOL CNS CTL ISA MCY MKY PPP ROK TSV WEI AVV ABX MEB HBA LST MBW MEL Australia ADL JAD KTA KGI KNX LEA PHE PER UMR XCH BWU CBR CFS CDU DBO NLK SYD TMW WGA PEK HLD TSN TYN CAN CSX KWL NNG SZX CGO WUH FNJ LHW XIY ULN JHG KMG XMN KHN FOC HGH NGB NKG HFE TAO SHA YNT CKG KWE CTU XIC KHG HTN URC HRB MDG DLC PVG TOD SZB NTQ HBE BTI LUR PIZ ITO ORL BTT Z84 UTO FYU SVW FRN TLJ CZF BED SNP EHM STG ILI PTU BMX OSC OAR MHR BYS NXP FSM MRI GNT PNC SVN GFK PBF NSE HNM PRC TTN BOS SUU RME ENV BFM OAK OMA NOW OGG ICT MCI MSN DLG HRO PHX BGR FXE GGG AND GEG HWO SFO CTB ARA GNV MEM DUG BIG CNW ANN CAR LRF HUA POB DHT DLF LAX ANB CLE DOV CVG FME NID HON JNU LFT EWR BOI INS GCK MOT HHI MXF DAL FCS HLN NKX LUF HHR HUL END NTD EDW LCH KOA MYR NLC ACK FAF HOP DCA NHK PSX BYH ACY TIK PUB PQI GRF ADQ UPP FLL MKO INL SLC CDS BIX LSF FRI MDT LNK LAN MUE MSS HKY SPG FMY IAH MLT ADW INT VCV CEW GTB PHN BFL ELP HRL CAE DMA NPA PNS RDR HOU BKF ORT PAQ PIT BRW EFD NUW ALI VAD MIA SEA CHA BDR JAN GLS LGB HDH IPT IND SZL AKC GWO HPN FOK JBR LNA NZY BIF YUM CNM DLH BET LOU FHU LIH HUF HVR MWH MPV RIC SHV CDV ORF BPT SAV HIF OME PIE MNM CXO SCC SAT ROC COF TEB RCA RDU DAY ENA MLC IAG CFD PHF ESF LTS TUS MIB BAB IKK GSB PVD SBY RIU BUR DTW TPA PMB POE EIL HIB LFK MAF GRB ADM WRI SBO AGS ISN LIT SWF BDE SAC HOM TBN MGE SKA HTL PAM DFW MLB TCM AUS LCK MQT TYS HLR STL MIV SPS LUK ATL MER MCC GRR INK FAT VRB IPL BNA LRD EDF OTZ AOO DYS ELD LGA TLH DPA ACT AUG INJ MKL MKK FTK SJT CXL CIC BTV JAX DRO IAD CLL SFF MKE ABI COU PDX TNT PBI FTW OGS BFI SKF HNL DSM EWN SAN MLU SSC ONT GVT ROW DET BRO DHN WWD MTC FMN CRP SYR NQX MDW SJC HOB PNE DEN PHL SUX MCN TCS PMD RND NJK CMH FYV FSI PNM FFO GAL MWL IAB NBG BFT TXK PBG APG TCC ANC GRK ZUN BLI NQA EKN HFD SFZ MOB NUQ SAF BKH DRI BSF OLS MCF BLV OPF DRT RSW AKN MUI JHM JFK HST RAL FLV WAL HMN NXX CYS SCK CHS RNO KTN YIP VBG BHM NEL LSV RIV MOD SMF UGN COS BUF SKY PAE MUO CDC BDL MFE NGU CEF LBB ORD BCT FAI NYG CVS NGF OFF GKN ART PSP AMA FOD BAD FOE COT ILM United States TYR BWI HBR LNY AEX WSD CDB TUL SIT ISP MSP ILG DUT MSY PWM OKC ALB VDZ LFI SNA CBM TMB NTU GUS CPR VPS SEM EYW CLT LAS MCO FLO GTF YNG FBK MMV WRB BKK NAH TTR KDI SBG MLG BDO CBN JOG CXP PCB SRG BTH TJQ PGK TNJ SIQ BDJ PKN PKY MOF ENE RTG KOE LBJ BPN TRK SRI AMI BMU WGP SUB SOC ICN CNX CEI NST NAK KOP UBP KKC THS DPS ATH NGO UKB PUW LWS ELM ITH MRY SBA DAB LPX RIX SQQ HLJ KUN PLQ VNO PNV EVN LWN ASA ASM MSW GZA BUS KUT TBS RIY TAI HOD ADE AXK AAY SAH BHN SCT FMM NAV EZE EBL EMD HEW KIX TAG JAV JCH JEG PMI DRW URT TKA GZM HVN AVL GSO FSD AYQ MHT APF RDN SDF CHO ROA LEX EVV ABQ BZN BIL BTM TVC FRS BHB RKD JAC RFD DME SYX MFN LJG GSP BMI GPT AZO TOL FWA DEC CID LSE CWA PIA ATW RST CMI MHK KGC HVB DLU MZV SSH FKL NBO SEU FTE ARM GJT SGU DWH S46 SRQ BDA VNY MLI PFN HIR PPT INU FUN OVB XKH BIS TEX INC HGN RAP CLD FNT DVO FNC STM KOS YOA NPE LEV LXA RDD EUG IDA MFR KBZ RDM PCN WDH YWH TNA CZX YBP TJM CAK HSV PKB MGM TRI PAH JIB HAK MFA PGA UII FCA MBS BGM BGW NNT ROI BFV TDX BLH CRK SDK LXG ODY SHE MNI PSG LYA XUZ MWQ KHM DLI VKG CAH VCL TBB PYY BWK NSI CKY AAH FKB SFB JST LUA BHP LDN JMO NGX PPL RUM DNP RUK JUM TPJ TMI SKH IMK DOP BJH DHI MWX JTY JIK JKL MLO JNX PAS KZS RMF NRN USU BXU DPL LAO LGP OZC CEB NOE JUI BPS PMW CLV MSO BKQ BDB GCN SGR HIS APA CVN FST LVS IWS LHX LRU BKD TPL OZA KDM LAK YWJ ZFN YGH TAH YPC SRZ SAB EGE SKN CGF MFD CSG LAW FNL FLG TVL TWF MVY CON GON STC BPE GTR GOJ HQM ERI HYA SDX MGW CRW AVP BJI THG FGI BNK FAR MKC RBE GCC TOF PHY CJM JZH SWA GEO AGT OGL KAI DNH AOI CPO TCP LYB BJV TBJ SAW SCE BME NTL KLU HFT HVG MEH VDS IKA MHD UIK MEI SPI CEZ HDN GUP LBL LAA GLD COD Norway ISC SGF NVK BVG Norway NSK AAQ JLN ABE XNA GUW KZO SBN BKA ARH RTW NUX NOJ SCO UCT USK PEX NNM PKV KGP KJA KGF URJ IWA CGQ KIJ JON SMD ACV OAJ TCL DBQ ATD AKS BAS FRE MBU IRA SCZ MUA GZO MNY RNL RUS VAO KGE RBV BUA CMU DAU GUR PNP HKN UNG KRI KMA KVG MDU MAS MXH MIS TIZ TBG RAB VAI WBM LLU CNP JFR JGO JJU JSU JNN JNS NAQ JHS JUV JQA GRY THO VPN YWS YAA YWM YFX YHA YRG YCK YLE SUR YAX WNN YNO XBE KIF YOG YHP YKU ZTB ZLT YAC YAG XKS YKG YAT YBE YBX YRF YCS YDP YER YFA YFH YMN YGB YGO YGT YGW YGX YGZ YQC CXH YNS YHO YHR YIK YIV AKV YKQ YPJ YLC YLH YSG XGR YMH YMT YUD YNC YNE YNL YOH YPH YPM YPO YPW YQD YQN YRA YRL YSF YSK YST YTL YVZ YWP YXN YZG ZAC ILF ZBF ZEM ZFD ZGI ZJN ZKE MSA ZMT ZPB ZRJ ZSJ ZTM ZUM ZWL BLJ CBH BMW ELU KMS HDF HEI HGL SJY NQT United Kingdom CAL EOI FIE NRL PPW SOY NDY LWK WRY LEQ PZE VLY BRR CFN CNL LKN OSY Norway Norway RET SDN SOG SVJ SOJ Norway VRY BZG LCJ OSD HFS KSD TYF AGH SQO HMV VTS QRA MQP AAM MBD GNZ ORP SWX TLD DIS CIP SLI YVA WAQ JVA BMD MXT TVA WTA WTS WAM WPB DWB WMP TTS WMA MJA CBT DUE VPE MSZ KOU MJL TCH VPY SRH CMK LUD OND OMD SWP ERS BOA MAT INO NIO KRZ BSU TSH LJA PFR GMZ BTE KBS KEN OXB SMW VIL ESU EUN NDR Cape Verde Cape Verde BCO BEI DSE DEM GDE GOR ABK MTF TIE ALU BSA MGQ GLK BUO AAC ATZ ASV LKG MYD NYK SRX TOB MJI LAQ ATB UYL PZU BKZ TKQ LDI MUZ SHY TBO RUA ULU DIU ABR ABY AHN ALM ALO ALW APN ATY BFD BFF BKW BQK BRL CEC CGI CIU CKB CLM CMX DDC DUJ EAU EKO EWB FAY GGW GRI HOT HTS KIO IRK JMS LAR LBE LBF LEB LMT LNS LWT LYH MKG MLS MSL OTH OWB PIB PIH PIR PLN PSM RDG RHI RKS RUT SBP SHR SLK SLN SMX TUP UIN VCT VLD WRL YKM ECN RJL IDY ANE LTT JSY PEV QGY SOB AOT QSR CVU BNX USQ KSY SFQ KCM AJI ADF ISE EDO SZF ILZ GDT MDS SLX AZS JBQ PBR AAZ UTK AHS PEU MIJ CYW CUA GUB JAL CTD ONX JQE PLP TTQ BCL TNO PBP PJM SYQ JEE PAX TND COX ATC CAT CRI PID AIU MGS MHX MUK MOI PYE ICI PTF KDV MNF MFJ NGI LKB LBS TVU KXF RTA SVU EUA HPA Tonga NTT VBV IUE FUT MXS APK AHE AUQ UAP UAH MTV SLH TOH EAE CCV LOD SSR PBJ LPM LNB MWF LNE NUS ZGU RCL SON TGH ULB VLS SWJ OLZ AUY AWD DLY FTA IPA TGJ BMY ILP FBD DWD AJF EWD KHD BXR RJN BJB AFZ NSH SRY LRR ADU OMH AAN BNP BHV CJL DBA DEA DSK JIW HDD KDD ORW PAJ KDU SYW TUK ISU KAC GXF ADK GST SGY HCR HNS KLG MCG MOU ANI VAK WRG LUP ENT LZN HCN MFK KUH OKD HSG NKM IWJ FKS ONJ SYO MYE KUV MPK WJU YNY HIN CJJ SFS CYU CGM JOL SGS SUG TDG WNP BSO SFE TUG VRC CYP CRM MBT RXS TTG LHS OES ING GGS SST NEC JDO LEC MEA MII VDC RIA TOW ESR ZPC SOD SCY LOH ESM PSY CRC GLJ LQM LPD NQU PDA EYP GYA PUR RIB REY SRJ ORG MVS LHC CJA HUU NZC SRA MDO MYC VIG JPR BBQ DSD BBR SFC GBJ NEV VIJ BQU UNI KOV PPK DZN UKK KSN KVD NAJ CNN PYJ CKH CYX IKS KXK DYR OHO UJE MPW VSG OZH KWG HRK IFO CWC RWN UDJ CSH CEE AMV KSZ PES GNA MVQ EIE KYZ NOZ HTG IAA GRV NAL OGZ ESL WKK BLF GLH PSC KQA LPS SLY HMA NYA OVS IJK KVX NYM RAT NFG KRO LBD AZN FEG NMA NCU UGC KSQ TMJ RYB EGO URS LPK VKT UUA JOK CSY ULY OSW PEZ SKX BWO HBX KCT WRZ BBM SHL GAU DMU TEZ BZL OUI BHR BDP MEY KEP GAN HAQ KDO MAQ BMV HPH CXR VCS VCA DIN UIH PXU VII BMO TVY KAW LIW MNU BSX PKK SWQ TMC BUI SEH TJS DTD BEJ TJG SMQ LUV ARD BLG LGL ODN MKM BKM LWY BBN TMG KUD TKG HLP NTX PSU SQG PDO LSW PKG LBW NNX LPU ALH GYL AUU BCI BDD BVI BHQ HTI BEU BRK BUC GIC OKY BQL BHS BLT CVQ CAZ CPD CNC CNJ CED CTN CMA CNB CUQ OOM DMD NLF DPO ELC EPR FLS GET GLT GTE GFF HID HOK MHU HGD JCK KAX KNS KFG KRB KWM KUG LNO LEL LDH IRG LSY LHG LRE LER LVO UBB MKR MIM MGT MNG MCV MQL MMG MRZ MOV MYA MGB ONG MYI MBH NRA NAA NTN ZNE OLP PUG PMK PBO CCK GOV PKE PLO EDR PQQ PTJ ULP RAM RMA SGO MJK SBR SRN XTG TCA VCD SYU WNR WYA WUN WOL WIN BWT OKR XMY NAY CIF CIH DAT HET BAV SJW TGO HLH XIL BHY CGD DYG MXZ ZUH LZH ZHA ENH NNY XFN YIH AKA GOQ HZG IQN XNN ENY UYN AVK LTI BYN DLZ HVD MXV DIG LUM SYM ZAT KOW JDZ JIU JUZ LYG HYN LYI JJN TXN WEF WEH WUX WUS WNZ YNZ YIW HSN BPX DAX GYS LZO MIG NAO LZY WXN AKU IQM KCA KRL KRY YIN HEK JMU JNZ NDG YNJ WKL WME LRV IOR NNR GTI NBB ORH AQG SHP YCU JGN DSN PWT SPW JEF UNT PVC SBH KMW SUI TBW OBN ERM CVF FUL NVI QSF LRH SUN MCW AZA XAU AKP ANV ATK GAM HPB KAL KSM KVL MYU RBY SHH SVA WTK OMC YPX YTQ ARC QOW FON TMU CYZ KVK GVR PJA QBC HGR ACR GOP SDP HMI WUZ TBH ACP GBT IIL PFQ TCG MQM AFS DRG LEN RGS EGM CQD DHM NDC SLV IGG KNW KVC PTH TOG EGN LKH WLH CHG UAS BHG YVB SKT PDP WVB MPA AOE CKZ MSR SIC TEQ YEI LSS KMV VQS YIF HDM MRQ GFN OAG TRO COQ HOJ ESC YAK GUL CES NSO DGE MTL CPX MWA BMQ OKB KIK IUD GBZ IMT AET MGC SWD GRM AUW MYP LKS MVA QSA WSY MIE LAF VGT ENW MTJ RIW PDT LYM PKH KTR Australia NOA UKC CEJ TNL BQT OSH AGE EAT ARE RIN UKX RMT QLS ZJI QNC GDZ IAR OHE JNG DRK NYT ZXB WUA GYY BRD LWB PGV CYF OXR TEN NIU SCH NBC IAO LGO NLP CKC UST NLV RHP STS ISM LCQ LGU BMC MLD ASE ULV ERV GED ZSW GBN HYS SUS LYU GPZ TVF EGV ARV IKV YBV NGP AVX MHV ZIN INQ SWT HUT STJ NDZ VOK GUC SIA TOA MBL PGD WFK JHW YTM SME SHD DVL DIK SDY CDR AIA MCK MTH GDV OLF WYS ALS CNY ELY VEL SRR SOW MYL SMN MMH FRD ESD AST ONP EMK UNK UUK SHX NUI EEK KUK KWT KWK MLL RSH KGK KMO CIK EAA HUS HSL NUL VEE WBQ CEM SHG IYK VIS MCE CYR CPQ TWB AYK AGN ELV FNR HNH AFE MTM HYG EGX KPV PIP WSN AKK KYK KLN ABL BKC IAN OBU ORV WLK KTS ELI GLV TLA WAA WMO KKA SMK SKK TNC AKB CYT AUK KPN KFP NLG PML KLW KWN KOT KYU SCM NNL KKH NIB AKI AIN APZ RDS PNT SGV IGB NCN TKJ IRC SLQ HKB AQC MHM MLY YSO YWB YTF YGV YXK XTL XLB XSI YBT ZGR YCR YRS YOP YBY ZNA YGG YJM YDT ZEL YFJ RNI BZA RFS SIU WSP PLD COZ NEG RVR SPB ARR JSM UYU ABF ABN DRJ ICK OEM SMZ TOT AGI CSC ORJ NAI IMB KAR USI MHA PJC ACD RVE VGZ EBG CAQ COG TLU CFB OPS GRP CMP BVS SFK PIN BRA STZ MQH AUX NVP LVR FRC DOU LBR ROO GPB JCB RVD AAX MBZ RBB CIZ BAZ DMT GNM QDJ NZA LBZ AMC GSQ MRB AWA JIJ MKS DBM DBT QHR GOB MYB MRE RBX CPA MAX BDI WHF PAF HTY RVV ILD BIU GJR SAK IIA TDK ULG VGD LDG HSK CQM NJF CSA RKH AGC FTY TSO TII ZAJ CCN FUG LCX ACX HZH OSU ADS DTS KHE SZS HJJ YQI ISO FFA CKS MWK PGU YES OSB TJH AXJ KKX AGJ UGA ULO BPR LBX TJU TAZ BWB DRB WGE BRT DKI LZR HLT HCQ FIZ RVT PVU SBS DTA RIF PUC LAM HII INW DGL MZK AEA AAK KUC AIS TMN BEZ NIG BBG MTK MNK NON TSU WTZ KTF AFT RNA CHY NNB XYA BOW FTI LVK RMY GFY NDU AGM TRM SMO UDD ZSY OLM DWA RIL SAA PDK BMG SUA MMU APC SDM VNC PHK ECP SBD VAL CAU AWK QNV SQL OSZ RWI SXQ SEE PHA SQH TKF FRJ GEX RYY 4U9 LVM 6S0 BIV HEN LAL SYH IDL RBK FNU MYQ PCW MGY RID FDY PEA EBE EMP ESX BBP SPF QYD OLV ONQ BJC SLE UTM ZKB LND MWC JVL GKY LZU BWG RVS NHD KGO DBB BCE CKL TCZ UKS JCI ESN HMR MYV DUC UVA LOT CCR OCA YUS YOO LHA SGH MSI HEX CDA JAB HGS TOP NGQ CSO TKI PWK KLS ZTA PUE KHC UKA ILN AVW CGZ BXK E63 MMI STK RWL YZY CDW AIZ TVI HSH GML TMA QXR DVT YGE YRV HDO ZHY MCL LHD PPC KHW TXG HLG FZG XYE DWC RKP MVV MFX OKF OKU PSH CKF OMN TTD HIO KHT GAI AZ3 YTA TSB YSD BNU CVX YCC IZA XFL MVL RBD WST BID NME OOK OBY VIN BGE ZKG YBI SPZ WHP MAE YZZ YAB BCV MPY LDX KJI CPB HMB RVY POJ JTC OIA RDC SXX BYO SXO CFC CAF ERN CCI FBE CFO AAF UMU DTI FBA OLC HUW IRZ ORX UNA TEF GZP DQH FRP TAN PYM OQU OWD BAF IJD MGJ CXY GHG DXR ASH LWM OXC FIT VPC PYP RMG GAD DKX WDR JYL DNN CTJ LGC MLJ PIM FFC GVL PHD UDG HHH DNL MRN PBX TOC PLV WUU HUE OYL WYE GBK AFW 57C RMK LGH RTS KEW YSP YHF YHN YKX YMG YXZ YEM YFD LWC EGT PMP XMC EET YUE LOP ZML HDG UMP LOZ FBG WMI JXA YGM EYK RAC RZP REI RIR TIW JKA HMJ HIW HZL CBE YBO KLF LNR JOT VYS JXN BBX OBE SEF AVO GIF ZPH OCF JES 52A CCO HQU AIK CDN LBT SOP RCZ DLL SVH BUU LHV LPR BKL DKK VAY LDJ ANQ VNW GVQ CLW CGX JZP CRE IGQ RNM BXO OEB WBW LNN UMD RLK FFT LEW MRK DRM GDW LWA MFI ISW CWI BVY YRQ POF EPM EOK PSL STP SOO VNA DKS BYT ADY GAS HOA KEY ILU ATJ OVA RGK FLD PCZ STE ERY PEF GQQ TPN PTZ CKV LPC CTH BST LLK GBB ZTU LKP DEE AOH DSO SSI BFP GGE HDI RNT POC CTY CEU BEC PDG GTU QFO SNY GKL RPB IFL BIN MOO ECA JYO VAM LLF LSZ ONS TDR SDC WBU BBJ PAO FFZ P08 P52 A39 E25 YTY PTK KSI EEN GKK RCS RHD KMP IOW TLQ MWM ANP FXO ODO ZTR HRI PEQ HBG QCJ QSC YKN XSB ZBM KTI GYU CNI KRH CCL HWD MZP JHQ ARB SHT TEM GAH WIO BFJ ULK GNY KZR VLU BEO 4A7 BMP YCN BJP BQB SEK IVR GLI IMM YIC PTB KGN SBM KFE BJU MZJ GEU SAD KJP SIK TTI GFL 5B2 CGC MTN LHM FZI IZG NEW COE BMT DNV COJ TIX NYE AAP FCM LIX OJC GIU EUM TKT YLK YEE NU8 ND4 NF4 YCM YPD OQN MNZ KFS 2H0 GXH CIY KVM ZKP UMS ADH NLT BOR FDW OBC TDJ AQB NOR BTZ DAW WAR EWK BSJ TZR FBR S40 CLS M94 EVW K83 LRO EUF 6J4 MEO AUO CZG EKY A50 MIC 23M DBN PUK CVO PXH CWT OGD W63 RKR AKO SHN WNA PKA YBW 2A5 WKR GFO DYL TGI TJL OAL OCW 7W6 MHC YEL UKF JZI DAN 0V4 ERG CQW BEM NKT SUY OUZ QUO KAA MBI SGX AOG ZYI HYW LDS AVA KSS WTB TNH SZV LII NTI WSR DTB SSV MEQ BUW KAZ MNA SGQ OKL KOX CMQ WMB RCM DCN KNO HBI EMV EMT FAH IXT I16 KRQ SSF JSV JAS 87K MRF E38 BQE CZA BUY CCB IOB EKI C03 CUB GMJ MPR GYH PXE HVS IGX SZT DU9 SUT FCI HNZ LEE PPY DIQ ERD ERL CNO HTR UYF BWW PRB HAF HCJ WJF CJF GUZ UBT BOX FYJ TDF PZL KJH HPG YIE HNY WOS IGT ASN GMU MKT NGD TOI SCD EDN ALX PKT GPN HZP UWL TQK HDE PTT AHQ LXN 19S CBF OKK 2K7 EHA FTG GBG GUY MEJ ULS IDP 3AU LQR LZZ BYY PRX CFV GXY OEL FET LGD SZY MPO UKT YBA BNG OFK AWO TFL TPF BZC ITP KBN IKL AIR JRN JIA VLP JUA CCX BMB QBX APQ FLB PCS BNC BNB MTP VPZ DRV SXK MLZ PDU ATI HSM SWH TTL KWB KOO AOU N/A SQX LDM RHV OHS RZS TMF IFU KIE YKO BUT TLI TQL BPL ACS WVI GLU JGG DLK KM8 XTO YSE MRT YAH YAL YBU YCE YCP YCQ XRR YDO YEY YGD YHE YHT YDG YJF YKJ YLR YME YML YOS YPS YQS YRO YRP YSH YSL YVD YVE YXQ YSN KES XPK ZGF ZJG YTD PIW XPP WPC ZST ZUC FNB FSS BXP DGP LMR SXN NDD MAI ADI MWE ALN AXN CLU BBD BIH BKE BPI WMH BTL BYI CCY CNU CRG CSV DAA DAG DMN DRA EED EGI EKA HYI HYR JCT LLQ LOL MBG MCB MDH MMT NHZ NRB OGB OLU OTM OZR PWY POU RNH RSL RWF SNS SOA SUZ TPH UAO UKI UOX HTV VTN WMC WWR XMR ZZV ECV CDT TEV TN0 QLP BJY RUV XPL UPL QSN SNJ KMX XXN KNR PYK XIJ MPD ATG SGI RQW ENN WWA QUT IEJ AAV BPH MXI VMR BAT QHP TOQ LGR CNR TLX ZIC TTC API CVE PAL PZA TQS MQZ RIJ JAE IKU VYI GVN NLI VLK INA ZIX RTC HIM RDP PUT ABH ARY BLN BZD BWQ CVC CWW CYG CMD DRN DNQ DYA ECH FRB GUH HXX HTU KRA KPS KGY TGN MRG RPM QRM PPI SIO SNB SWC TYB TUM WGT WKB QRR SXE WWY NGA LHK WDS HTT UUN BFU RUG WHU SXJ YKH ================================================ FILE: share/salt/README.md ================================================ # Opinionated example of deployment via Salt Stack ## Assumptions: * user & group srv:srv exist, this is used as a generic service runner * 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) * You have, or are willing to deploy Salt Stack. * A bit of assembly is required since you need to move pillar.sls into your saltroot/pillar/ and the rest into saltroot/wttr/ * You want metric-sm units. Just roll your own wegorc to change this ## Caveats: * Doesn't do enough to make a recent master checkout work, i.e. needs further improvement. Latest known working revision is 0d76ba4a3e112694665af6653040807835883b22 ================================================ FILE: share/salt/init.sls ================================================ wttr: service.running: - enable: True - watch: - file: /srv/ephemeral/start.sh - git: wttr-repo - require: - pkg: wttr-dependencies - git: wttr-repo - cmd: wego - archive: geolite-db # package names are from Ubuntu 18.04, you may need to adjust if on a different distribution wttr-dependencies: pkg.installed: - pkgs: - golang - gawk - python-setuptools - python-dev - python-dnspython - python-geoip2 - python-geopy - python-gevent - python-flask - python-pil - authbind wttr-repo: git.latest: - name: https://github.com/chubin/wttr.in - rev: master - target: /srv/ephemeral/wttr.in - require: - /srv/ephemeral wttr-start: file.managed: - name: /srv/ephemeral/start.sh - source: salt://wttr/start.sh - mode: '0770' - user: srv - group: srv wegorc: file.managed: - name: /srv/ephemeral/.wegorc - user: srv - group: srv - source: salt://wttr/wegorc - template: jinja - context: apikey: {{ pillar['wttr']['apikey'] }} geolite-db: archive.extracted: - name: /srv/ephemeral - source: http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz - source_hash: http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz.md5 - keep_source: True - options: --strip-components=1 # flatten directory structure - enforce_toplevel: False # Could benefit from improvement, won't get updated automatically at all wego: cmd.run: - onlyif: 'test ! -e /srv/ephemeral/bin/wego' - env: - GOPATH: /srv/ephemeral - name: go get -u github.com/schachmat/wego && go install github.com/schachmat/wego - cwd: /srv/ephemeral/ - require: - pkg: wttr-dependencies - file: wegorc /srv/ephemeral: file.directory: - makedirs: True {% for dir in '/srv/ephemeral/wttr.in/log','/srv/ephemeral/wttr.in/cache' %} {{ dir }}: file.directory: - user: srv - group: srv - makedirs: True - recurse: - user - group - require_in: - service: wttr {% endfor %} /etc/systemd/system/wttr.service: file: - managed - source: salt://wttr/wttr.service - require: - file: wttr-start - file: authbind-80 - require_in: - service: wttr authbind-80: file: - managed - name: /etc/authbind/byport/80 - user: srv - group: srv - mode: 770 - replace: False - require: - pkg: wttr-dependencies ================================================ FILE: share/salt/pillar.sls ================================================ wttr: apikey: insert-api-key-here-and-make-this-pillar-available-to-salt ================================================ FILE: share/salt/start.sh ================================================ #!/bin/sh export WEGORC="/srv/ephemeral/.wegorc" export GOPATH="/srv/ephemeral" export WTTR_MYDIR="/srv/ephemeral/wttr.in" export WTTR_GEOLITE="/srv/ephemeral/GeoLite2-City.mmdb" export WTTR_WEGO="$GOPATH/bin/wego" export WTTR_LISTEN_HOST="0.0.0.0" export WTTR_LISTEN_PORT="80" python $WTTR_MYDIR/bin/srv.py ================================================ FILE: share/salt/wegorc ================================================ # wego configuration # # This config has https://github.com/schachmat/ingo syntax. # Empty lines or lines starting with # will be ignored. # All other lines must look like "KEY=VALUE" (without the quotes). # The VALUE must not be enclosed in quotes as well! # aat-frontend: Show geo coordinates (default false) aat-coords=false # aat-frontend: Monochrome output (default false) aat-monochrome=false # BACKEND to be used (default forecast.io) backend=forecast.io # NUMBER of days of weather forecast to be displayed (default 3) days=3 # forecast backend: the api KEY to use (default ) forecast-api-key={{ apikey }} # forecast backend: print raw requests and responses (default false) forecast-debug=false # forecast backend: the LANGUAGE to request from forecast.io (default en) forecast-lang=en # FRONTEND to be used (default ascii-art-table) frontend=ascii-art-table # json frontend: do not indent the output (default false) jsn-no-indent=false # LOCATION to be queried (default 40.748,-73.985) location=40.748,-73.985 # openweathermap backend: the api KEY to use (default ) owm-api-key= # openweathermap backend: print raw requests and responses (default false) owm-debug=false # openweathermap backend: the LANGUAGE to request from openweathermap (default en) owm-lang=en # UNITSYSTEM to use for output. # Choices are: metric, imperial, si, metric-ms (default metric) units=metric-ms # worldweatheronline backend: the api KEY to use (default ) wwo-api-key= # worldweatheronline backend: print raw requests and responses (default false) wwo-debug=false # worldweatheronline backend: the LANGUAGE to request from worldweatheronline (default en) wwo-lang=en ================================================ FILE: share/salt/wttr.service ================================================ [Unit] Description=Wttr weather service [Service] ExecStart=/usr/bin/authbind --deep /srv/ephemeral/start.sh Restart=always [Install] WantedBy=multi-user.target ================================================ FILE: share/screenrc ================================================ screen -t srv.py bash -c "cd ~/wttr.in; ve/bin/python bin/srv.py; bash -i" screen -t proxy.py bash -c "cd ~/wttr.in; ve/bin/python bin/proxy.py; bash -i" ================================================ FILE: share/scripts/build-welang.sh ================================================ #!/usr/bin/env bash # The scipr is used to build a standalone we-lang binary from # the wttr.in Go source code. The script is needed as long as # a standalone we-lang binary is used. # # The script requires a configured Go compiler in PATH. show_usage() { cat < main.go package main func main() { Cmd() } EOF } if [[ -z $1 ]]; then show_usage exit 0 fi DST_FILE="$1" DIR=$(mktemp -d /tmp/build-welang-XXXXXXXXX) cp -R "internal/view/v1" "$DIR/v1" sed -i 's/^package .*/package main/' "$DIR"/v1/*.go cd "$DIR"/v1 || fatal "can't change into the build directory" go mod init github.com/chubin/we-lang || fatal "Can't do 'go mod init'" go mod tidy || fatal "Can't do 'go mod tidy'" add_main go build -o "$DST_FILE" *.go || fatal "Building error" rm -rf "$DIR" ================================================ FILE: share/scripts/clean-cache.sh ================================================ #!/bin/bash LOGFILE=/tmp/clean-cache.log _log() { echo "$(date +"[%Y-%m-%d %H:%M:%S]") $*" >> "$LOGFILE" } _log_pipe() { while read -r line; do _log "$line" done } CACHEDIR="/wttr.in/cache" _log Cleaning up the cache. _log Before: df -h "$CACHEDIR" | _log_pipe for dir in wego proxy-wwo png lru do mv "${CACHEDIR}/${dir}" "${CACHEDIR}/${dir}.old" mkdir "${CACHEDIR}/${dir}" rm -rf "${CACHEDIR}/${dir}.old" done _log After: df -h "$CACHEDIR" | _log_pipe _log "=============================" cd /wttr.in/log mv main.log main.log.1 touch main.log ================================================ FILE: share/scripts/log-space.sh ================================================ #!/usr/bin/env bash LOG_DIR="/wttr.in/log" LOG_FILE="$LOG_DIR/diskspace.log" DISK=/wttr.in log() { mkdir -p "$LOG_DIR" echo "$(date +"[%Y-%m-%d %H:%M:%S]") $*" | tee -a "$LOG_FILE" } log $(df -k "$DISK" | tail -1 | awk '{print $4}') ================================================ FILE: share/scripts/start-screen.sh ================================================ #!/bin/bash SESSION_NAME=wttr.in SCREENRC_PATH=$(dirname $(dirname "$0"))/screenrc screen -dmS "$SESSION_NAME" -c "$SCREENRC_PATH" ================================================ FILE: share/static/malformed-response.html ================================================ wttr.in
Sorry, we processed more than 1M requests today and we ran out of our datasource capacity.
We hope to solve the problem as soon as possible, so you can enjoy
your favourite weather service 24x365 even if it rains or snows.

We will solve the problem as soon as possible.
 for the updates. 

If you like to code (and you surely do), you can check the wttr.in repository
to see how the scalability problem is (not yet) solved.

================================================ FILE: share/static/style.css ================================================ body { background: black; color: #bbbbbb; } /* Switch to light mode if the user prefers it */ /* @media (prefers-color-scheme: light) { body { background-color: #ffffff; color: #000000; } } */ pre { /* font-family: source_code_proregular; */ /* font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace; font-size: 70%; */ /*font-family: Lucida Console,Lucida Sans Typewriter,monaco,Bitstream Vera Sans Mono,monospace; */ /*Droid Sans Mono*/ font-family: "Source Code Pro", "DejaVu Sans Mono", Menlo, "Lucida Sans Typewriter", "Lucida Console", monaco, "Bitstream Vera Sans Mono", monospace; /*font-family: bitstream_vera_sans_monoroman;*/ font-size: 75%; } ================================================ FILE: share/systemd/README.md ================================================ To add **wttr.in** to systemd as a service, do the following steps. 1. **Create a systemd service file**: You’ll need to create a service file in `~/.config/systemd/user/` (for user-level) or `/etc/systemd/system/` (for system-wide) directory. Let’s create it for a user. Create the directory if it doesn’t exist: ```bash mkdir -p ~/.config/systemd/user/ ``` Then, create the service file called `myscript.service`: ```bash cp share/systemd/wttrin.service ~/.config/systemd/user/wttrin.service ``` 2. **Reload the systemd daemon**: This will ensure systemd recognizes the new service. ```bash systemctl --user daemon-reload ``` 4. **Enable and start your service**: To start the service immediately, run: ```bash systemctl --user start wttrin.service ``` 5. **Check the status**: To verify that your service is running correctly, you can check its status: ```bash systemctl --user status wttrin.service ``` 6. **Start service automatically**: This will ensure the service is running after reboot. To enable it to start automatically at boot, run: ```bash systemctl --user enable wttrin.service ``` Enable user services even if the user is not logged in (specify the user name instead of `USER`): ```bash sudo loginctl enable-linger USER ``` ================================================ FILE: share/systemd/wttrin.service ================================================ [Unit] Description=wttr.in services [Service] Type=oneshot RemainAfterExit=yes ExecStart=/home/igor/src/wttr.in/share/systemd/wttrin.sh session1 ExecStop=/usr/bin/tmux kill-session -t session1 [Install] WantedBy=default.target ================================================ FILE: share/systemd/wttrin.sh ================================================ #!/usr/bin/env bash SESSION_NAME="" SRC_DIR=/home/igor/src/wttr.in SERVICES_FILE=config/services/services.yaml start_service() { local name="$1" local workdir="$2" local cmd="$3" if [[ -z "$SESSION_NAME" ]]; then echo Unknown SESSION_NAME. Exiting >&2 exit 1 fi local WINDOW_NAME="$name" local TEXT_TO_ENTER="cd $workdir && $cmd" if ! tmux has-session -t "$SESSION_NAME" >& /dev/null; then tmux new-session -d -s "$SESSION_NAME" fi # Create a new window if it doesn't exist tmux list-windows -t "$SESSION_NAME" | grep -q "^.\+ $WINDOW_NAME " || tmux new-window -t "$SESSION_NAME" -n "$WINDOW_NAME" sleep 0.05 # Send text to the new window and press Enter tmux send-keys -t "$SESSION_NAME:$WINDOW_NAME" "$TEXT_TO_ENTER" C-m } main() { local name local cmd if [[ -n $1 ]]; then SESSION_NAME=$1 else echo Usage: $0 SESSION_NAME >&2 exit 1 fi cd "$SRC_DIR" || exit 1 while read -r line; do name=$(jq -r .name <<< "$line") workdir=$(jq -r .workdir <<< "$line") cmd=$(jq -r .command <<< "$line") start_service "$name" "$workdir" "$cmd" done <<< "$(yq -c .services[] < "$SERVICES_FILE")" } main "$@" ================================================ FILE: share/templates/index.html ================================================
{{ body }}
================================================ FILE: share/test-thunder.txt ================================================ Prévisions météo pour: Test-Milon, France  _`/"".-.  Averse de pluie légère  ,\_( ).  17 °C  /(___(__)  ↙ 5 km/h  ‘ ‘ ‘ ‘  14 km  ‘ ‘ ‘ ‘  3.6 mm ┌─────────────┐ ┌──────────────────────────────┬───────────────────────┤ sam. 09 juin├───────────────────────┬──────────────────────────────┐ │ Matin │ Après-midi └──────┬──────┘ Soir │ Nuit │ ├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤ │ Brume │ Nuageux │  _`/"".-.  Foyers orageux │  _`/"".-.  Averse de plui…│ │  _ - _ - _ -  21 °C │  .--.  25-26 °C │  ,\_( ).  25-27 °C │  ,\_( ).  22 °C │ │  _ - _ - _  ← 5-6 km/h │  .-( ).  ← 4-5 km/h │  /(___(__)  ↑ 4-6 km/h │  /(___(__)  → 5-9 km/h │ │  _ - _ - _ -  19 km │  (___.__)__)  18 km │  ⚡‘‘⚡‘‘  16 km │  ‘ ‘ ‘ ‘  15 km │ │ 0.0 mm | 0% │ 0.0 mm | 0% │  ‘ ‘ ‘ ‘  0.7 mm | 46% │  ‘ ‘ ‘ ‘  2.4 mm | 82% │ └──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘ ┌─────────────┐ ┌──────────────────────────────┬───────────────────────┤ dim. 10 juin├───────────────────────┬──────────────────────────────┐ │ Matin │ Après-midi └──────┬──────┘ Soir │ Nuit │ ├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤ │  \ / Partiellement …│  \ / Partiellement …│  _`/"".-.  Averse de plui…│  _`/"".-.  Averse de plui…│ │  _ /"".-.  22-24 °C │  _ /"".-.  25-27 °C │  ,\_( ).  24-26 °C │  ,\_( ).  21 °C │ │  \_( ).  ↙ 9-10 km/h │  \_( ).  ↙ 13-19 km/h │  /(___(__)  ↙ 17-30 km/h │  /(___(__)  ↙ 15-31 km/h │ │  /(___(__)  20 km │  /(___(__)  20 km │  ‚‘‚‘‚‘‚‘  17 km │  ‚‘‚‘‚‘‚‘  18 km │ │ 0.0 mm | 0% │ 1.8 mm | 56% │  ‚’‚’‚’‚’  4.1 mm | 82% │  ‚’‚’‚’‚’  1.6 mm | 72% │ └──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘ ┌─────────────┐ ┌──────────────────────────────┬───────────────────────┤ lun. 11 juin├───────────────────────┬──────────────────────────────┐ │ Matin │ Après-midi └──────┬──────┘ Soir │ Nuit │ ├──────────────────────────────┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────┤ │  _`/"".-.  Pluie éparse à │ Nuageux │  _`/"".-.  Averse de plui…│  _`/"".-.  Averse de plui…│ │  ,\_( ).  21 °C │  .--.  24-26 °C │  ,\_( ).  23-25 °C │  ,\_( ).  20 °C │ │  /(___(__)  ↑ 14-23 km/h │  .-( ).  ↑ 18-22 km/h │  /(___(__)  ↗ 13-23 km/h │  /(___(__)  ↗ 10-21 km/h │ │  ‘ ‘ ‘ ‘  18 km │  (___.__)__)  18 km │  ‚‘‚‘‚‘‚‘  14 km │  ‚‘‚‘‚‘‚‘  14 km │ │  ‘ ‘ ‘ ‘  0.1 mm | 22% │ 0.0 mm | 0% │  ‚’‚’‚’‚’  5.2 mm | 73% │  ‚’‚’‚’‚’  5.3 mm | 72% │ └──────────────────────────────┴──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘ ================================================ FILE: share/translation.txt ================================================ wttr.in is translated in NUMBER_OF_LANGUAGES languages: SUPPORTED_LANGUAGES Translated/improved/corrected by: * Afrikaans: Casper Labuschage @casperl (on github) * Arabic Besher Aladdam @akai54 (on github), Ahmed D. ALi @_nakanakaii (twitter) / @nakanakaii (github) * Arhamic Robel Kassa @rawbubble * Armenian: Aram Bayadyan @aramix, Mikayel Ghazaryan @mkdotam, Grigor Khachatryan @grigortw * Azerbaijani: Dmytro Nikitiuk, Elsevar Abbasov, Eldar Velibekov (@welibekov on github) * Basque: Iker Sagasti (@isagasti on github) * Belarusian: Igor Chubin, Anton Zhavoronkov @edogby (on github) * Bosnian: Ismar Kunc @ismarkunc * Bengali: Nazia Tasnim (@appledora on github) * Bulgarian: Vladimir Vitkov @zeridon (on github) * Brazilian-PT: Tupã Negreiros @TupaNegreiros (on github), Gabriel Monteiro @krosct (on github) * Catalan: Angel Jarabo @legna29A * Chinese(Traditional): Jeff Huang * Chinese(Simplified): YouthLin Chen(@YouthLin on github), Junhan (@junhan-z on github), Felix Wong (@gnowxilef on github) * Croatian: Siniša Kusić @ku5ic * Czech: Juraj Kakody, Ondřej Povolný (@Remogeus on github) * Danish: Kim Schulz @kimusan (on github) * Dutch: Youri Claes, Edwin Martin @edwinm * Esperanto: Igor Chubin * Estonian: Jaan Jänesmäe @janesmae (on github) * Finnish: @Maxifi * French: Igor Chubin, @daftaupe, @iago-lito, @Marc66 (on github) * Frisian: Anne Douwe Bouma @anned20 (on github) * Galician Diego Blanco @diego-treitos (on github) * German: Igor Chubin, @MAGICC (https://kthx.at), Simon Neutert @simonneutert (on github) * Greek: Panayotis Vryonis and @Petewg (on github) * Hebrew: E.R. * Hindi: Aakash Gajjar @skyme5 (gh) * Hungarian: Mark Petruska * Icelandic: Óli G. @dvergur, Skúli Arnlaugsson @Arnlaugsson * Indonesian: Andria Arisal @andria009 * Interlingua: Dustin Redmond @dustinkredmond (on github) * Irish: Robert Devereux @RobertDev (gh), Conor O'Callaghan @ivernus (gh) * Italian: Diego Maniacco, Riccardo Volpe aka YITA @yitaverse (gh) * Japanese: @ryunix * Kazakh: Akku Tutkusheva, Oleg Tropinin * Korean: Jeremy Bae @opt9, Jung Winter @res_tin * Latvian: Gunārs Danovskis, Edgars Andersons (GitHub: @eandersons) * Lithuanian Juras Rutavičius * Macedonian: Matej Plavevski @MatejMecka * Marathi: Sanket Pandit Garade (@sanketgarade on github) * Norwegian: Fredrik Fjeld @fredrikfjeld * Nynorsk: Kevin Brubeck Unhammer (https://unhammer.org/k/) * Occitan: Quentin PAGÈS @Quenty-tolosan (gh) * Persian: Javad @threadripper_, @kagetora66 (on github) * Polish: Wojtek Łukasiewicz @wojtuch (on github) * Portuguese: Fernando Bitti Loureiro @fbitti (on github), Hugo Carvalho (@hugok79 on github) * Romanian: Gabriel Moruz * Russian: Igor Chubin, @layerex (on github), Vladimir Kalashnikov * Serbian: Milan Stevanović @FathVader * Slovak: Juraj Kakody * Slovenian: B.S. * Spanish: Fernando Bitti Loureiro @fbitti (on github) * Swedish: John Eriksson * Swahili: Joel Mukuthu * Tamil: Parthiban @parthi1984 (on github) * Telugu: Pavan Srinivas Mamidala @pavansrinivasmamidala (on github), Vishal Boddu @bodduv (on github) * Turkish: Atabey Kaygun, Yilmaz @edigu, Volkan Tokmak(@volkanto), Oğuz Ersen * Thai: Vatunyoo Suwannapisit @kerlos * Ukrainian: Igor Chubin, Serhiy @pavse * Uzbek: Shukhrat Mukimov * Vietnamese: Lưu Vĩnh Phúc @phuclv90 (on github) * Welsh: Daniel Thomas and many many others. In the not likely case wttr.in is not yet translated in your language you can help with the translation. Please start with one of the following steps: * Open a GitHub issue: github.com/chubin/wttr.in * Write an email to igor@chub.in * Write PM or tweet to igor_chubin ================================================ FILE: share/translations/af-help.txt ================================================ Gebruik: $ curl wttr.in # huidige ligging $ cur wttr.in/muc # weerberig in München se lughawe Ondersteunde soorte ligging: /paris # stadsnaam /~Eiffel+tower # enige ligging /Москва # Unikode naam van enige ligging in enige taal /muc # lughawe kode (3 letters) /@stackoverflow.com # domeinnaam /94107 # gebiedskodes /-78.46,106.79 # GPS koördinate Besondere liggings: /moon # Fase van die maan (voeg +US of ,+France by vir daardie stede) /moon@2016-10-25 # Fase van die maan vir die datum (@2016-10-25) Eenhede: m # metrike (SI) (word orals gebruik behalwe vir die VSA) u # USCS (gebruik in die VSA) M # vertoon die spoed van die wind in m/s Besigtig keuses: 0 # slegs die huidige weer 1 # huidige weer + 1 dag 2 # huidige weer + 2 dae F # moet nie die "Volg" lyn wys nie n # smal weergawe (slegs dag en nag) q # stil weergawe (geen "Weerberig" teks nie) Q # baie stil weergawe (geen "Weerberig), geen stadsnaam T # skakel terminaal kodes af (geen kleur) PNG keuses: /paris.png # skep 'n PNG leêr p # voeg 'n raam by rondom die uitset t # deursigtigheid 150 transparency=... # deursigtigheid vanaf 0 tot 255 (255 = nie deursigtig nie) Keuses can saamgevoeg word /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # met PNG word die lêer mode daarna gegee /Rome_0pq_lang=it.png # lang keuses word met 'n onderstreping verdeel Lokalisering: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Ondersteunde tale: FULL_TRANSLATION (word ondersteun) PARTIAL_TRANSLATION (in wording) Spesiale URLe: /:help # vertoon hierdie bladsy /:bash.function # vertoon aanbeveling vir bash funksie wttr() /:translation # vertoon die besonderhede van die vertalers ================================================ FILE: share/translations/af.txt ================================================ 113: Helder : Clear 113: Sonnig : Sunny 116: Gedeeltelik bewolk : Partly cloudy 119: Bewolk : Cloudy 122: Oortrokke : Overcast 143: Mis toestande : Mist 176: Gedeeltelike reën moontlik : Patchy rain possible 179: Gedeeltelike sneeu moontlik : Patchy snow possible 182: Gedeeltelike ysreën moontlik : Patchy sleet possible 185: Gedeeltelik ysige motreën moontlik : Patchy freezing drizzle possible 200: Donderbuie moontlik : Thundery outbreaks possible 227: Waaiende sneeu : Blowing snow 230: Sneeustorm : Blizzard 248: Mis toestande : Fog 260: Vriesende mis toestande : Freezing fog 263: Gedeeltelike ligte motreën : Patchy light drizzle 266: Ligte motreën : Light drizzle 281: Vriesende motreën : Freezing drizzle 284: Erg vriesende motreën : Heavy freezing drizzle 293: Gedeeltelike ligte reën : Patchy light rain 296: Ligte reën : Light rain 299: Matige reën by tye : Moderate rain at times 302: Matige reën : Moderate rain 305: Swaar reën by tye : Heavy rain at times 308: Swaar reën : Heavy rain 311: Ligte vriesende reën : Light freezing rain 314: Matige tot swaar vriesende reën : Moderate or heavy freezing rain 317: Ligte ysreën : Light sleet 320: Matige tot swaar ysreën : Moderate or heavy sleet 323: Verspreide ligte sneeu : Patchy light snow 326: Ligte sneeu : Light snow 329: Verspreide ligte sneeu : Patchy moderate snow 332: Matige sneeu : Moderate snow 335: Verspreide swaar sneeu : Patchy heavy snow 338: Swaar sneeu : Heavy snow 350: Haelkorrels : Ice pellets 353: Ligte bui reën : Light rain shower 356: Matige tot swaar bui reënbui : Moderate or heavy rain shower 359: Stortreën : Torrential rain shower 362: Ligte buie ysreën : Light sleet showers 365: Ligte tot swaar buie ysreën : Moderate or heavy sleet showers 368: Ligte buie sneeu : Light snow showers 371: Ligte tot swaar buie sneeu : Moderate or heavy snow showers 386: Verspreide ligte buie en donderbuie : Patchy light rain with thunder 389: Ligte tot swaar buie en donderbuie : Moderate or heavy rain with thunder 392: Verspreide ligte sneeu met donderbuie : Patchy light snow with thunder 395: Matige of swaar sneeu met donderbuie : Moderate or heavy snow with thunder ================================================ FILE: share/translations/am-help.txt ================================================ አጠቃቀም: $ curl wttr.in # አሁን ያሉበት አካባቢ $ curl wttr.in/add # የአየር ሁነታ በቦሌ አዲስ አበባ አለምአቀፍ ኤርፖርት ተቀባይነት ያላቸው ቦታዎች: /paris # የከተማ ስም /~Eiffel+tower # ማንኛውም ቦታ /Москва # nom Unicode ou emplacement quelconque dans n'importe quelle langue /muc # የኤርፖርት ኮድ (3 lettres) /@stackoverflow.com # nom de domaine /94107 # የፖስታ ኮድ (ለአሜሪካ ብቻ) /-78.46,106.79 # coordonnées GPS Emplacements particuliers: /moon # phases de la lune (ajouter ,+US ou ,+France pour accéder aux villes du même nom) /moon@2016-10-25 # phases de la lune pour cette date (@2016-10-25) Unités: ?m # système métrique (par défaut partout sauf pour les États-Unis d'Amérique) ?u # USCS (par défaut pour les États-Unis d'Amérique) ?M # affiche la vitesse du vent en m/s Option d'affichage: ?0 # aujourd'hui seulement ?1 # aujourd'hui + demain ?2 # aujourd'hui + 2 jours ?n # version courte (seulement le jour et la nuit) ?q # version silencieuse (pas d'en-tête "Prévisions météo pour") ?Q # version super-silencieuse (pas d'en-tête "Prévisions météo pour", pas de nom de la ville) ?T # séquences d'échappement pour terminaux désactivées (pas de couleurs) የPNG አማራጮች: /paris.png # génère un fichier PNG ?p # ajoute un cadre autour de la sortie ?t # transparency 150 (transparence 150) transparency=... # transparence de 0 à 255 (255 = pas de transparence) Combiner les options: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # dans le mode PNG les options sont spécifiées après _ /Rome_0pq_lang=it.png # les options longues sont séparées par des underscores _ Localisation: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris ቋንቋዎች: FULL_TRANSLATION (ሙሉ በሙሉ የተተረጎሙ) PARTIAL_TRANSLATION (በከፊል የተተረጎሙ) URLs particulières: /:help # affiche cette page /:bash.function # fonction bash recommandée wttr() /:translation # affiche les informations sur la traduction de wttr.in ================================================ FILE: share/translations/am.txt ================================================ 113: ጥርት ያለ : Clear 113: ፀሐያማ : Sunny 116: ከፊል ደመናማ : Partly cloudy 119: ደመናማ : Cloudy 122: ደመናማ : Overcast 143: ጭጋግ : Mist 176: ዝናብ ሊዘንብ ይችላል : Patchy rain possible 179: በረዶ ሊጥል ይችላል : Patchy snow possible 182: አልፎ አልፎ በረዶ አዘል ዝናብ ሊዘንብ ይችላል : Patchy sleet possible 185: አልፎ አልፎ በረዶ አዘል ካፍያ ሊጥል ይችላል : Patchy freezing drizzle possible 200: : Thundery outbreaks possible 227: ነፋሻማ በረዶ : Blowing snow 230: በረዶ አዘል አውሎ ነፋስ : Blizzard 248: ጭጋግ : Fog 260: በረዶ ጭጋግ : Freezing fog 263: ቀላል ካፍያ : Patchy light drizzle 266: ቀላል ካፍያ : Light drizzle 281: በረዶ ካፍያ : Freezing drizzle 284: ከባድ በረዶ ካፍያ : Heavy freezing drizzle 293: አልፎ አልፎ ቀላል ዝናብ : Patchy light rain 296: ቀላል ዝናብ : Light rain 299: አልፎ አልፎ መለስተኛ ዝናብ : Moderate rain at times 302: መለስተኛ ዝናብ : Moderate rain 305: አልፎ አልፎ ከባድ ዝናብ : Heavy rain at times 308: ከባድ ዝናብ : Heavy rain 311: ቀላል የበረዶ ዝናብ : Light freezing rain 314: መለስተኛ ወይም ከባድ የበረዶ ዝናብ : Moderate or heavy freezing rain 317: : Light sleet 320: : Moderate or heavy sleet 323: አልፎ አልፎ ቀላል በረዶ : Patchy light snow 326: ቀላል በረዶ : Light snow 329: አልፎ አልፎ መለስተኛ በረዶ : Patchy moderate snow 332: መለስተኛ በረዶ : Moderate snow 335: አልፎ አልፎ ከባድ በረዶ : Patchy heavy snow 338: ከባድ በረዶ : Heavy snow 350: የበረዶ እንክብሎች : Ice pellets 353: ቀላል የዝናብ ካፍያ : Light rain shower 356: መለስተኛ ወይም ከባድ የዝናብ ካፍያ : Moderate or heavy rain shower 359: ዶፍ ዝናብ : Torrential rain shower 362: : Light sleet showers 365: : Moderate or heavy sleet showers 368: ቀላል የበረዶ ካፍያ : Light snow showers 371: መለስተኛ ወይም ከባድ የበረዶ ካፍያ : Moderate or heavy snow showers 386: ቀላል ዝናብ ከነጎድጓድ ጋር : Patchy light rain with thunder 389: መለስተኛ ወይም ከባድ ዝናብ ከነጎድጓድ ጋር : Moderate or heavy rain with thunder 392: ቀላል በረዶ ከነጎድጓድ ጋር : Patchy light snow with thunder 395: መለስተኛ ወይም ከባድ በረዶ ከነጎድጓድ ጋር : Moderate or heavy snow with thunder ================================================ FILE: share/translations/ar-help.txt ================================================ الإستخدام: $ curl wttr.in # الموقع الحالي $ curl wttr.in/muc # الطقس في مطار ميونخ أنواع الأماكن المدعومة: /paris # اسم المدينة /~Eiffel+tower # أي مكان /Москва # اسم يونيكود ﻷي مكان بأي لغة /muc # الاسم النمطي للمطار (3 احرف) /@stackoverflow.com # اسم النطاق /94107 # رمز المنطقة /-78.46,106.79 # GPS إحداثيات الـ الأماكن الخاصة: /moon # مرحلة القمر (أضف ,+US أو ,+France لمدينة معينة) /moon@2016-10-25 # مرحلة القمر بتاريخ (2016-10-25) الوحدات: m # النظام المتريّ (SI) (يستخدم في العادة في كل الأماكن ما عدا الولايات المتحدة) u # وحدات القياس العرفية الأمريكية (يستخدم في العادة في الولايات المتحدة) M # إظهار سرعة الرياح بوحدة م/ث خيارات العرض: 0 # الطقس الحالي فقط 1 # الطقس الحالي وطقس اليوم 2 # الطقس الحالي وطقس اليوم ويوم غد A # تجاهل الوكيل المستخدم وقم بتطبيق نمط ANSI (الطرفية) F # "لا تظهر سطر "المتابعة n # النسخة الخفيفة جدا (النهار والليل فقط) q # النسخة الصامتة (من غير عبارة "تقرير جوي") Q # النسخة الصامتة كليا (بدون عبارة "تقرير جوي" أو اسم المدينة) T # العودة لنمط تسلسل الطرفية (من غير ألوان) PNG خيارات: /paris.png # PNG إنشاء صورة بصيغة p # إضافة إطار حول المخرج t # الشفافية 150 transparency=... # الشفافية من 0 إلي 255 (255 = غير شفاف) background=... # لون الخلفية بنمط RRGGBB يمكن جمع الخيارات: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # يتم تحديد خيار ملف PNG بعد الشرطة السفلية /Rome_0pq_lang=it.png # الخيارات الطويلة يتم فصلهم عن طريق شرطة سفلية الترجمة: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris اللغات المدعومة: FULL_TRANSLATION (مدعومة) PARTIAL_TRANSLATION (جار العمل) روابط URLs خاصة : /:help # عرض هذه الصفحة /:bash.function # wttr() bash عرض الميزة الخاصةبـ /:translation # عرض المعلومات حول المترجمين ================================================ FILE: share/translations/ar.txt ================================================ 113: صاف : Clear 113: مشمس : Sunny 116: غائم جزئياً‏ : Partly cloudy 119: غائم : Cloudy 122: ملبد بالغيوم : Overcast 143: شبورة : Mist 176: احتمال هطول أمطار متفرقة : Patchy rain possible 179: احتمال هطول ثلوج متفرقة : Patchy snow possible 182: احتمال هطول المطر الثلجي متفرقة : Patchy sleet possible 185: احتمال هطول أمطار متفرقة : Patchy freezing drizzle possible 200: احتمال ظهور الرعد : Thundery outbreaks possible 227: هبوب ثلجية : Blowing snow 230: عاصفة ثلجية : Blizzard 248: ضباب : Fog 260: ضباب بارد : Freezing fog 263: رذاذ خفيف متقطع : Patchy light drizzle 266: رذاذ خفيف : Light drizzle 281: رذاذ بارد : Freezing drizzle 284: رذاذ غزير بارد : Heavy freezing drizzle 293: أمطار خفيفة متفرقة : Patchy light rain 296: أمطار خفيفة : Light rain 299: أمطار معتدلة أحياناً : Moderate rain at times 302: أمطار معتدلة : Moderate rain 305: أمطار كثيفة أحياناً : Heavy rain at times 308: أمطار كثيفة : Heavy rain 311: أمطار باردة خفيفة : Light freezing rain 314: أمطار باردة معتدلة أو كثيفة : Moderate or heavy freezing rain 317: مطر ثلجي خفيف : Light sleet 320: مطر ثلجي معتدل أو كثيف : Moderate or heavy sleet 323: ثلوج خفيفة متفرقة : Patchy light snow 326: ثلوج خفيفة : Light snow 329: ثلوج معتدلة متفرقة : Patchy moderate snow 332: ثلوج معتدلة : Moderate snow 335: ثلوج كثيفة متفرقة : Patchy heavy snow 338: ثلوج كثيفة : Heavy snow 350: حبيبات جليدية : Ice pellets 353: رذاذ مطر خفيف : Light rain shower 356: رذاذ مطر معتدل أو كثيف : Moderate or heavy rain shower 359: رذاذ مطر غزير : Torrential rain shower 362: رذاذ مطر ثلجي خفيف : Light sleet showers 365: رذاذ مطر ثلجي معتدل أو كثيف : Moderate or heavy sleet showers 368: رذاذ ثلجي خفيف : Light snow showers 371: رذاذ ثلجي معتدل أو كثيف : Moderate or heavy snow showers 386: أمطار خفيفة متفرقة مصحوبة بالرعد : Patchy light rain with thunder 389: أمطار معتدلة أو كثيفة متفرقة مصحوبة بالرعد : Moderate or heavy rain with thunder 392: ثلوج خفيفة متفرقة مصحوبة بالرعد : Patchy light snow with thunder 395: ثلوج معتدلة أو كثيفة متفرقة مصحوبة بالرعد : Moderate or heavy snow with thunder ================================================ FILE: share/translations/az.txt ================================================ 113: Aydınlı : Clear : Ясно 113: Günəşli : Sunny : Солнечно 116: Dəyişkən buludlu : Partly cloudy : Переменная облачность 119: Buludlu : Cloudy : Облачно 122: Buludlu : Overcast : Пасмурно 143: Çən : Mist : Дымка 176: Bəzi yerlərdə yagış : Patchy rain possible : Местами дождь 179: Bəzi yerlərdə qar : Patchy snow possible : Местами снег 182: Bəzi yerlərdə qar və yagış : Patchy sleet possible : Местами дождь со снегом 185: Bəzi yerlərdə soyuq çisklinli hava : Patchy freezing drizzle possible : Местами замерзающая морось 200: Bəzi yerlərdə tufan : Thundery outbreaks possible : Местами грозы 227: Yüngül küləkli qar : Blowing snow : Поземок 230: Çovqun : Blizzard : Метель 248: Duman : Fog : Туман 260: Güclü soyuq duman : Freezing fog : Переохлажденный туман 263: Bəzi yerlərdə zəif çiskin : Patchy light drizzle : Местами слабая морось 266: zəif çiskin : Light drizzle : Слабая морось 281: Soyuq çiskin : Freezing drizzle : Замерзающая морось 284: Güclü soyuq çiskin : Heavy freezing drizzle : Сильная замерзающая морось 293: Bəzi yerlərdə yüngül yagış : Patchy light rain : Местами небольшой дождь 296: Yüngül yagiş : Light rain : Небольшой дождь 299: Bəzi vaxtlarda mülayim yagış : Moderate rain at times : Временами умеренный дождь 302: Mülayim yagış : Moderate rain : Умеренный дождь 305: Bəzi vaxtlarda güclü yagış : Heavy rain at times : Временами сильный дождь 308: Güclü yagış : Heavy rain : Сильный дождь 311: Zəif soyuq yagış : Light freezing rain : Слабый переохлажденный дождь 314: Mülayim yaxud güclü soyuq yagış : Moderate or heavy freezing rain : Умеренный или сильный переохлажденный дождь 317: Yüngül yagış və qar : Light sleet : Небольшой дождь со снегом 320: Mülayim yaxud güclü yagış və qar : Moderate or heavy sleet : Умеренный или сильный дождь со снегом 323: Bəzi yerlərdə yüngül qar : Patchy light snow : Местами небольшой снег 326: Yüngül qar : Light snow : Небольшой снег 329: Bəzi yerlərdə mülayim qar : Patchy moderate snow : Местами умеренный снег 332: Mülayim qar : Moderate snow : Умеренный снег 335: Bəzi yerlərdə güclü qar : Patchy heavy snow : Местами сильный снег 338: Güclü qar : Heavy snow : Сильный снег 350: Buzlu yagış : Ice pellets : Ледяной дождь 353: Yüngül leysanlı yagış : Light rain shower : Небольшой ливневый дождь 356: Mülayim yaxud güclü leysanlı yagış : Moderate or heavy rain shower : Умеренный или сильный ливневый дождь 359: Çox güclü leysan : Torrential rain shower : Очень сильный ливень 362: Yüngül leysanlı yagış və sulu qar : Light sleet showers : Небольшой ливневый дождь со снегом 365: Yüngül qar : Moderate or heavy sleet showers : Небольшой снег 368: Yüngül leysanlı yagış və qar : Light snow showers : Небольшой ливневый дождь со снегом 371: Mülayim yaxud güclü qarlı leysan : Moderate or heavy snow showers : Умеренный или сильный снежный ливень 386: Bəzi yerlərdə yüngül tufan və yagış: Patchy light rain with thunder : Местами небольшой дождь с грозой 389: Mülayim yaxud güclü yagış və tufan : Moderate or heavy rain with thunder : Умеренный или сильный дождь с грозой 392: Bəzi yerlərdə yüngül tufan və qar : Patchy light snow with thunder : Местами небольшой снег с грозой 395: Mülayim yaxud güclü qar və tufan : Moderate or heavy snow with thunder : Умеренный или сильный снег с грозой ================================================ FILE: share/translations/be-help.txt ================================================ Выкарыстанне: $ curl wttr.in # надвор'е ў вашым месцы $ curl wttr.in/msq # надвор'е ў аэрапорце Мінск Магчымыя тыпы месцазнаходжання: /novaja-mysz # населены пункт /~dipgorodok # якое-небудзь месца /жабінка # назва якога-небудзь месца юнікодам на нейкай мове /gme # трохлітарны код аэрапорта /@osm.by # даменнае імя /220121 # паштовы індэкс /53.953994,27.537774 # каардынаты GPS Адмысловыя месцазнаходжанні: /moon # Фаза месяца (дадайце ,+US або ,+France для горада Moon у ЗША або ў Францыі) /moon@2016-10-25 # Фаза Месяца для гэтай даты (@2016-10-25) Адзінкі вымярэння: m # метрычная (СІ) (звычайна ўжываецца паўсюль акрамя ЗША) u # USCS (ўжываецца ў ЗША) M # паказваць хуткасць ветру ў м/с Опцыі прагляду: 0 # толькі надвор'е цяпер 1 # надвор'е цяпер + 1 дзень 2 # надвор'е цяпер + 2 дні A # не зважаць на User-Agent і мусова ўжываць фармат вываду ANSI (як у тэрмінале) F # не паказваць радок "Сачыце за навінамі" n # вузкая версія (толькі дзень і ноч) q # ціхая версія (без тэксту "Прагноз надвор'я для") Q # надта ціхая версія (без "Прагноз надвор'я для", без назвы месца) T # выключыць паслядоўнасці тэрміналу (без колераў) Опцыі PNG: /szklow.png # выдаць файл PNG p # дадаць рамку вакол t # празрыстасць 150 transparency=... # празрыстасць ад 0 да 255 (255 = непразрыста) Опцыі можна спалучаць: /iuje?pq /dzvinsk?0pq&lang=be /bielastok_0pq.png # для PNG опцыі падаюцца пасля знаку _ /turau_0pq_lang=be.png # доўгія опцыі адасабляюцца знакам _ Лакалізацыя: $ curl be.wttr.in/Minsk $ curl wttr.in/minsk?lang=be $ curl -H "Accept-Language: be" wttr.in/minsk Даступныя мовы: FULL_TRANSLATION (падтрымліваюцца) PARTIAL_TRANSLATION (не цалкам) Адмысловыя адрасы: /:help # паказаць гэтую старонку /:bash.function # паказаць рэкамендаваную функцыю bash wttr() /:translation # паказаць спіс перакладаў ================================================ FILE: share/translations/be.txt ================================================ 113: Ясна : Clear : 113: Сонечна : Sunny : 116: Пераменная воблачнасць : Partly cloudy : 119: Воблачнасць : Cloudy : 122: Хмарна : Overcast : 143: Смуга : Mist : 176: Месцамі дождж : Patchy rain possible : 179: Месцамі снег : Patchy snow possible : 182: Месцамі дождж са снегам : Patchy sleet possible : 185: Месцамі сцюдзёная імжа : Patchy freezing drizzle possible : 200: Месцамі навальніцы : Thundery outbreaks possible : 227: Нізавая завіруха : Blowing snow : 230: Мяцеліца : Blizzard : 248: Туман : Fog : 260: Сцюдзёны туман : Freezing fog : 263: Месцамі невялікая імжа : Patchy light drizzle : 266: Невялікая імжа : Light drizzle : 281: Сцюдзёная імжа : Freezing drizzle : 284: Моцная сцюдзёная імжа : Heavy freezing drizzle : 293: Месцамі невялікі дождж : Patchy light rain : 296: Невялікі дождж : Light rain : 299: Часам умераны дождж : Moderate rain at times : 302: Умераны дождж : Moderate rain : 305: Часам моцны дождж : Heavy rain at times : 308: Моцны дождж : Heavy rain : 311: Невялікі сцюдзёны дождж : Light freezing rain : 314: Умераны ці моцны сцюдзёны дождж : Moderate or heavy freezing rain : 317: Невялікі дождж са снегам : Light sleet : 320: Умераны ці моцны дождж са снегам : Moderate or heavy sleet : 323: Месцамі невялікі снег : Patchy light snow : 326: Невялікі снег : Light snow : 329: Месцамі умераны снег : Patchy moderate snow : 332: Умераны снег : Moderate snow : 335: Месцамі моцны снег : Patchy heavy snow : 338: Моцны снег : Heavy snow : 350: Ледзяны дождж : Ice pellets : 353: Слабы ліўневы дождж : Light rain shower : 356: Умераны ці моцны ліўневы дождж : Moderate or heavy rain shower : 359: Вельмі моцны ліўневы дождж : Torrential rain shower : 362: Невялікі ліўневы дождж са снегам : Light sleet showers : 365: Умераны ці моцны ліўневы дождж са снегам : Moderate or heavy sleet showers : 368: Невялікі ліўневы снег : Light snow showers : 371: Умераны ці моцны ліўневы снег : Moderate or heavy snow showers : 386: Месцамі невялікі дождж з навальніцай : Patchy light rain with thunder : 389: Умераны ці моцны дождж з навальніцай : Moderate or heavy rain with thunder : 392: Месцамі невялікі снег з навальніцай : Patchy light snow with thunder : 395: Умераны ці моцны снег з навальніцай : Moderate or heavy snow with thunder : ================================================ FILE: share/translations/bg-help.txt ================================================ Употреба: $ curl wttr.in # текущо местоположение $ curl wttr.in/sof # времето на Софийското летище Поддържани типове местоположения: /paris # име на град /~Eiffel+tower # място/забележителност /Москва # Юникод име на местоположение /sof # Код на летище (3 букви) /@stackoverflow.com # име на домейн /94107 # пощенски код /-78.46,106.79 # GPS координати Специални местоположение: /moon # Фаза на луната (добавете ,+US or ,+France за тези места) /moon@2016-10-25 # Фаза на луната за дата (@2016-10-25) Мерни единици: m # метрични (SI) (по подразбиране навсякъде извън US) u # USCS (използвани по подразбиране в US) M # покажи скоростта на вятъра в m/s Възможности на изгледа: 0 # само текущото време 1 # текущо време + 1 ден 2 # текущо време + 2 дни A # игнорирай User-Agent и форсирай ANSI формат (за терминал) F # не показвай реда "Следвай" n # тясна версия (само ден и нощ) q # тиха версия (без текст "Прогноза за времето") Q # свръх тиха версия(без "Прогноза за времето", без име на местоположение) T # изключи терминалните настройки (без цветове) PNG възможности: /paris.png # генериране на PNG p # Сложи рамка около резултата t # полупрозрачен фон 150 transparency=... # полупрозрачен фон от 0 до 255 (255 = непрозрачно) Опциите могат да се комбинират: /Paris?0pq /Paris?0pq&lang=bg /Paris_0pq.png # при генериране на PNG възможностите са отделени с _ /Rome_0pq_lang=bg.png # дългите опции са разделени с подчертавка Локализация: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=bg $ curl -H "Accept-Language: bg" wttr.in/paris Поддържани преводи: FULL_TRANSLATION (Напълно преведени) PARTIAL_TRANSLATION (В процес на превод) Специални Адреси (URL): /:help # тази страница /:bash.function # препоръчителна bash функция wttr() /:translation # покажи информация за преводачите ================================================ FILE: share/translations/bn-help.txt ================================================ ব্যবহার: $ curl wttr.in # এখন যেখানে আছ $ curl wttr.in/cdg # প্যারিস - চার্লস ডি গল বিমানবন্দরে আবহাওয়ার পূর্বাভাস গৃহীত কমান্ডের ধরন: /paris # শহরের নাম /~Eiffel+tower # যেকোনো স্থানের নাম /Москва # ইউনিকোড নাম বা যেকোনো ভাষায় যেকোনো স্থানের নাম /muc # বিমানবন্দর কোড (3 অক্ষর) /@stackoverflow.com # ডোমেন নাম /94107 # জিপ কোড (শুধুমাত্র মার্কিন যুক্তরাষ্ট্রে) /-78.46,106.79 # জিপিএস স্থানাঙ্ক বিশেষ কমান্ড: /moon # চাঁদের পর্যায়গুলি (একই নামের শহরগুলি অ্যাক্সেস করতে যোগ করুন, + US বা, + France) /moon@2016-10-25 # এই তারিখের জন্য চাঁদের পর্যায়গুলি (@ 2016-10-25) ইউনিট: ?m # মেট্রিক সিস্টেম (মার্কিন যুক্তরাষ্ট্র ছাড়া সব জায়গায় ডিফল্ট) ?u # USCS (মার্কিন যুক্তরাষ্ট্রের জন্য ডিফল্ট) ?M # বাতাসের গতি m / s তে প্রদর্শন করে বিকল্প প্রদর্শন : ?0 # শুধুমাত্র আজ ?1 # আজ + আগামীকাল ?2 # আজ + 2 দিন ?n # সংক্ষিপ্ত সংস্করণ (শুধুমাত্র দিন এবং রাত) ?q # নীরব সংস্করণ (কোন "আবহাওয়ার পূর্বাভাস" হেডার নেই) ?Q # অতি-নীরব সংস্করণ (কোন "আবহাওয়ার পূর্বাভাস" হেডার নেই, শহরের নাম নেই) ?T # ডিজেবল্ড টার্মিনালের জন্য এস্কেপ সিকুএন্সে (কোনও রঙ নেই) বিকল্প PNG: /paris.png # একটি PNG ফাইল তৈরি করুন ?p # আউটপুটের চারপাশে একটি ফ্রেম যুক্ত করুন ?t # স্বচ্ছতা 150 (স্বচ্ছতা 150) transparency=... # 0 থেকে 255 পর্যন্ত স্বচ্ছতা (255 = অস্বছ) বিকল্প একত্রিত করুন: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # পিএনজি মোডে বিকল্পগুলি _ এর পরে নির্দিষ্ট করা হয় /Rome_0pq_lang=it.png # দীর্ঘ বিকল্পগুলি আন্ডারস্কোর দ্বারা পৃথক করা হয় _ ভাষান্তর : $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris সাপোরটেড ভাষাসমূহ : FULL_TRANSLATION (সম্পূর্ণ সাপোর্ট ) PARTIAL_TRANSLATION (অসম্পূর্ণ সাপোর্ট ) বিশেষ ইউআরএলসমূহ: /:help # এই পেইজটি প্রদর্শন করুন /:bash.function # রেকমেন্ডেড ব্যাশ ফাংশন wttr () /:translation # wttr.in এর অনুবাদ সম্পর্কে তথ্য প্রদর্শন করুন ================================================ FILE: share/translations/bn.txt ================================================ : বজ্রঝড়ের সঙ্গে ভারী বৃষ্টি ও শিলাবৃষ্টি : Heavy rain and hail with thunderstorm : বজ্রঝড়ের সঙ্গে ভারী বৃষ্টি : Heavy rain with thunderstorm : বজ্রঝড়ের সঙ্গে হালকা বৃষ্টি ও শিলাবৃষ্টি : Light rain and hail with thunderstorm : হালকা বৃষ্টি আর তুষারপাত : Light rain and snow shower : বজ্রবিদ্যুৎ সহ হালকা বৃষ্টি : Light rain with thunderstorm : হালকা তুষারপাত : Light snow shower : আংশিক কুয়াশা : Partial fog : বজ্রবিদ্যুৎ সহ বৃষ্টি ও শিলাবৃষ্টি : Rain and hail with thunderstorm : বজ্রবিদ্যুৎ সহ বৃষ্টি : Rain with thunderstorm : অগভীর কুয়াশা : Shallow fog : ধোঁয়া : Smoke : আকস্মিক ঝড়ো বাতাস : Squalls : আশেপাশে বজ্রঝড় : Thunderstorm in vicinity : তুষার : Snow : বৃষ্টি : Rain : হালকা বৃষ্টি, ঝরনা বৃষ্টি : Light Rain, Rain Shower : মাঝারী বৃষ্টিপাত : Rain Shower : টুকরো টুকরো কুয়াশা : Patches of fog : গুঁড়ি গুঁড়ি বৃষ্টি : Drizzle : হালকা গুঁড়ি গুঁড়ি বৃষ্টি : Light drizzle : হালকা ভাসমান তুষার : Low drifting snow : হালকা বৃষ্টি ও তুষারপাত : Light rain and snow : আশেপাশে বৃষ্টিপাত : Shower in vicinity : বজ্রবিদ্যুৎ সহ বৃষ্টি : Rain with thunderstorm : বৃষ্টি ও তুষারপাত : Rain and snow shower : বজ্রঝড় : Thunderstorm : গুঁড়ি গুঁড়ি মাঝারী বৃষ্টি : Drizzle and rain : বজ্রঝড়ের সঙ্গে শিলাবৃষ্টি : Hail with thunderstorm : কুয়াশা : Haze : হালকা গুঁড়ি গুঁড়ি বৃষ্টি : Light drizzle and rain : হালকা বৃষ্টি এবং বজ্রঝড় সহ ছোট শিলাবৃষ্টি/তুষারপাত : Light rain and small hail/snow pallets with thunderstorm 113 : পরিষ্কার : Clear 113 : রৌদ্রজ্জ্বল : Sunny 116 : আংশিক মেঘলা : Partly cloudy 119 : মেঘলা : Cloudy 122 : মেঘাচ্ছন্ন : Overcast 143 : কুয়াশা : Mist 176 : অল্প বৃষ্টি হতে পারে : Patchy rain possible 179 : অল্প তুষারপাত হতে পারে : Patchy snow possible 182 : অল্প শিলাবৃষ্টি হতে পারে : Patchy sleet possible 185 : ঠাণ্ডা হিমশীতল বৃষ্টির সম্ভাবনা : Patchy freezing drizzle possible 200 : বজ্রপাতের প্রাদুর্ভাবের সম্ভাবনা : Thundery outbreaks possible 227 : উড়ন্ত তুষার : Blowing snow 230 : তুষারঝড় : Blizzard 248 : কুয়াশা : Fog 260 : হিমশীতল কুয়াশা : Freezing fog 263 : খণ্ড খণ্ড হালকা গুঁড়ি গুঁড়ি বৃষ্টি : Patchy light drizzle 266 : হালকা গুঁড়ি গুঁড়ি বৃষ্টি : Light drizzle 281 : হিমশীতল গুঁড়ি গুঁড়ি বৃষ্টি : Freezing drizzle 284 : ভারী হিমশীতল গুঁড়ি গুঁড়ি বৃষ্টি : Heavy freezing drizzle 293 : খণ্ড খণ্ড হালকা বৃষ্টি : Patchy light rain 296 : হালকা বৃষ্টি : Light rain 299 : মাঝে মাঝে মাঝারি বৃষ্টি : Moderate rain at times 302 : মাঝারি বৃষ্টি : Moderate rain 305 : মাঝে মাঝে ভারী বৃষ্টি : Heavy rain at times 308 : ভারী বৃষ্টি : Heavy rain 311 : হিমশীতল হালকা বৃষ্টি : Light freezing rain 314 : মাঝারি বা ভারী হিমায়িত বৃষ্টি : Moderate or heavy freezing rain 317 : হালকা শিলা : Light sleet 320 : মাঝারি বা ভারী শিলাবৃষ্টি : Moderate or heavy sleet 323 : খণ্ড খণ্ড হালকা তুষারপাত : Patchy light snow 326 : হালকা তুষারপাত : Light snow 329 : খণ্ড খণ্ড মাঝারি তুষারপাত : Patchy moderate snow 332 : মাঝারি তুষারপাত : Moderate snow 335 : খণ্ড খণ্ড ভারী তুষারপাত : Patchy heavy snow 338 : ভারী তুষারপাত : Heavy snow 350 : বরফ প্যালেট : Ice pellets 353 : হালকা বৃষ্টির ঝরনা : Light rain shower 356 : মাঝারি বা ভারী বৃষ্টির ঝরনা : Moderate or heavy rain shower 359 : মুষলধারে বৃষ্টি : Torrential rain shower 362 : হালকা বরফমিশ্রিত ঝিরঝির বৃষ্টি : Light sleet showers 365 : মাঝারি বা ভারী বরফমিশ্রিত ঝিরঝির বৃষ্টি : Moderate or heavy sleet showers 368 : হাল্কা তুষারপাত : Light snow showers 371 : মাঝারি বা ভারী তুষারপাত : Moderate or heavy snow showers 386 : বজ্রসহ খণ্ড খণ্ড হালকা বৃষ্টি : Patchy light rain with thunder 389 : বজ্রসহ মাঝারি বা ভারী বৃষ্টি : Moderate or heavy rain with thunder 392 : বজ্রপাত সহ খণ্ড খণ্ড হালকা তুষারপাত : Patchy light snow with thunder 395 : মাঝারি বা ভারী তুষারপাত সহ বজ্রপাত : Moderate or heavy snow with thunder ================================================ FILE: share/translations/bs.txt ================================================ 113 : Vedro : Clear 113 : Sunčano : Sunny 116 : Djelomična naoblaka : Partly cloudy 119 : Oblačno : Cloudy 122 : Tmurno : Overcast 143 : Magla : Mist 176 : Moguća je slaba kiša : Patchy rain possible 179 : Moguć slab snijeg : Patchy snow possible 182 : Moguća je slaba susnježiva : Patchy sleet possible 185 : Moguća je ledena sitna kiša : Patchy freezing drizzle possible 200 : Moguća je grmljavina : Thundery outbreaks possible 227 : Topljenje snijega : Blowing snow 230 : Snježna mećava : Blizzard 248 : Magla : Fog 260 : Ledena magla : Freezing fog 263 : Mjestimično sitna kiša : Patchy light drizzle 266 : Sitna kiša : Light drizzle 281 : Ledena sitna kiša : Freezing drizzle 284 : Teško zamrzavanje : Heavy freezing drizzle 293 : Mjestimično slaba kiša : Patchy light rain 296 : Blaga kiša : Light rain 299 : Ponekad umjerena kiša : Moderate rain at times 302 : Umjerena kiša : Moderate rain 305 : Ponekad je kiša : Heavy rain at times 308 : Pljusak : Heavy rain 311 : Kruta kiša : Light freezing rain 314 : Umjerena ili teška hladna kiša : Moderate or heavy freezing rain 317 : Lagana susnježica : Light sleet 320 : Umjerena ili jaka susnježica : Moderate or heavy sleet 323 : Mjestimično slab snijeg : Patchy light snow 326 : Slab snijeg : Light snow 329 : Mjestimično blag snijeg : Patchy moderate snow 332 : Umjeren snijeg : Moderate snow 335 : Umjereno jak snijeg : Patchy heavy snow 338 : Jak snijeg : Heavy snow 350 : Grad/tuča : Ice pellets 353 : Lagani pljusak : Light rain shower 356 : Umjeren ili jak pljusak : Moderate or heavy rain shower 359 : Prolom oblaka : Torrential rain shower 362 : Slaba kiša sa gradom : Light sleet showers 365 : Umjerena ili jaka kiša sa gradom : Moderate or heavy sleet showers 368 : Blaga susnježica : Light snow showers 371 : Umjerena ili jaka susnježica : Moderate or heavy snow showers 386 : Slaba kiša s grmljavinom : Patchy light rain with thunder 389 : Umjerena ili jaka kiša s grmljavinom : Moderate or heavy rain with thunder 392 : Slabi snijeg s grmljavinom : Patchy light snow with thunder 395 : Umjereni ili teški snijeg s grmljavinom : Moderate or heavy snow with thunder ================================================ FILE: share/translations/ca-help.txt ================================================ Instruccions: $ curl wttr.in # el clima de la ubicació actual $ curl wttr.in/muc # el clima de l'aeroport de Múnich Tipus d'ubicacions suportades: /paris # el nom d'una ciutat /~Eiffel+tower # el nom de qualsevol lloc famós /Москва # el nom Unicode de qualsevol lloc en qualsevol idioma /muc # el codi d'un aeroport (3 lletres) /@stackoverflow.com # el nom d'un domini web /94107 # un codi d'àrea /-78.46,106.79 # coordenades de GPS Llocs especials: /moon # la fase de la lluna (afegeix ,+US o ,+France per a aquests països) /moon@2016-10-25 # la fase de la lluna en una data especfica (@2016-10-25) Unitats: ?m # mètriques (SI) (l'estàndard a tots els llocs excepte als EEUU) ?u # Sistema Unificat de Classificació del Sòl o USCS (l'estàndard als EEUU) ?M # mostrar la velocitat del vent en m/s Opcions de visualització: ?0 # noms el clima actual ?1 # el clima actual + la previsió d'1 dia ?2 # el clima actual + la previsió de 2 dies ?n # versió curta (només el dia i la nit) ?q # versió silenciosa (sense el text de "El temps a") ?Q # versió supersilenciosa (ni "El temps a" ni el nom de la ciutat) ?T # desactiva les seqüències del terminal (sense colors) Opcions de PNG: /paris.png # genera una imatge PNG ?p # afegeix una vora al voltant de la imatge ?t # transparència 150 transparency=... # transparència de 0 a 255 (255 = sense transparència) Les opcions es poden utilitzar conjuntament: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # amb PNG les opcions s'especifiquen després del caràcter _ /Rome_0pq_lang=it.png # una llarga seqüència d'opcions es poden separar amb el caràcter _ Ubicació: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Idiomes suportats: FULL_TRANSLATION (soportados) PARTIAL_TRANSLATION (en progreso) URLs especials: /:help # mostra aquesta pàgina /:bash.function # suggereix una funció wttr() en bash /:translation # mostra informació sobre els traductors ================================================ FILE: share/translations/ca.txt ================================================ 113 : Clar : Clear 113 : Assolellat : Sunny 116 : Parcialment ennuvolat : Partly cloudy 119 : Ennuvolat : Cloudy 122 : Molt ennuvolat : Overcast 143 : Boirina : Mist 176 : Possible pluja intermitent : Patchy rain possible 179 : Possible neu intermitent : Patchy snow possible 182 : Possible aiguaneu intermitent : Patchy sleet possible 185 : Possible plugim gelat intermitent : Patchy freezing drizzle possible 200 : Possible tempesta elèctrica : Thundery outbreaks possible 227 : Tempesta de neu : Blowing snow 230 : Torb : Blizzard 248 : Boira : Fog 260 : Boira gebradora : Freezing fog 263 : Plugim suau intermitent : Patchy light drizzle 266 : Plugim suau : Light drizzle 281 : Plugim gelat : Freezing drizzle 284 : Plugim gelat intens : Heavy freezing drizzle 293 : Pluja suau intermitent : Patchy light rain 296 : Pluja suau : Light rain 299 : Pluja moderada puntual : Moderate rain at times 302 : Pluja moderada : Moderate rain 305 : Pluja forta puntual : Heavy rain at times 308 : Pluja forta : Heavy rain 311 : Calamarsa : Light freezing rain 314 : Pedra o granís : Moderate or heavy freezing rain 317 : Aiguaneu suau : Light sleet 320 : Aiguaneu moderada o forta : Moderate or heavy sleet 323 : Nevada suau intermitent : Patchy light snow 326 : Nevada suau : Light snow 329 : Nevada moderada intermitent : Patchy moderate snow 332 : Nevada moderada : Moderate snow 335 : Nevada forta intermitent : Patchy heavy snow 338 : Nevada forta : Heavy snow 350 : Gebra : Ice pellets 353 : Ruixats de pluja suau : Light rain shower 356 : Ruixats de pluja moderada o forta : Moderate or heavy rain shower 359 : Ruixats de pluja torrencial : Torrential rain shower 362 : Ruixats d'aiguaneu suau : Light sleet showers 365 : Ruixats d'aiguaneu moderada o forta : Moderate or heavy sleet showers 368 : Ruixats de neu suau : Light snow showers 371 : Nevada moderada o forta : Moderate or heavy snow showers 386 : Intervals de pluges amb trons : Patchy light rain with thunder 389 : Pluja moderada o forta amb trons : Moderate or heavy rain with thunder 392 : Nevada lleugera intermitent amb trons : Patchy light snow with thunder 395 : Nevada moderada o forta amb trons : Moderate or heavy snow with thunder ================================================ FILE: share/translations/cs-help.txt ================================================ Použití: $ curl wttr.in # lokální informace $ curl wttr.in/muc # počasí v letšiti v Mnichově Podporované typy lokací: /paris # název města /~Eiffel+tower # jakákoliv lokace (+ pro mezery) /Москва # Unicode názvy pro lokace (včetně češtiny) /muc # kód letiště (3 písmena) /@stackoverflow.com # doména /94107 # kód areálu /-78.46,106.79 # GPS souřadnice Speciální lokace: /moon # Fáze měsíce (přidejte ,+US nebo ,+France pro tyto města) /moon@2016-10-25 # Fáze měsíce pro datum (@2016-10-25) Jednotky: m # metrické (SI) (used by default everywhere except US) u # USCS (used by default in US) M # zobrazení rychlosti větru v m/s Nastavení zobrazení: 0 # pouze momentální počasí 1 # momentální počasí + dnešní předpověď 2 # momentální počasí + dnešní + zítřejší předpověď A # ignorovat User-Agent a používát ANSI výstup (terminál) F # nezobrazovat řádek "Odebírat" n # zmenšená verze (jen den a noc) q # tichá verze (bez textu "Předpověď počasí") Q # extra tichá verze (bez textu "Předpověď počasí" a názvu města) T # vypnutí barev nastavení PNG: /paris.png # vygeneruje PNG p # přidá rámeček t # průhlednost 150 transparency=... # průhlednost od 0 do 255 (255 = neprůhledný) Nastavení se dá kombinovat: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # v PNG se nastavení přidává za _ /Rome_0pq_lang=it.png # dlouhé nastavení se odděluje _ Lokalizace: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Podporované jazyky: ar af be ca da de el et fr fa hi hu ia id it nb nl oc pl pt-br ro ru tr th uk vi zh-cn zh-tw (supported) az bg bs cy cs eo es eu fi ga hi hr hy is ja jv ka kk ko ky lt lv mk ml nl fy nn pt pt-br sk sl sr sr-lat sv sw te uz zh zu he (in progress) Speciální URL: /:help # tato stránka /:bash.function # zobrazí doporučenou bash funkci wttr() /:translation # zobrazí informace o překladech ================================================ FILE: share/translations/cs.txt ================================================ : Unášený sníh : Low drifting snow : Částečná mlha : Patches of fog : Mrholení : Drizzle : Slabé mrholení : Light drizzle : Sníh : Snow : Déšť : Rain : Lehký déšť : Light Rain : Dešťová sprcha : Rain Shower 113: Jasno : Clear 113: Slunečno : Sunny 116: Částečně zataženo : Partly cloudy 119: Zataženo : Cloudy 122: Zamračeno : Overcast 143: MLha : Mist 176: Možnost deště : Patchy rain possible 179: Možnost sněžení : Patchy snow possible 182: Možnost kroup : Patchy sleet possible 185: Možnost mrazivého mrholení : Patchy freezing drizzle possible 200: : Thundery outbreaks possible 227: Padající sníh : Blowing snow 230: Vánice : Blizzard 248: Mlha : Fog 260: Mrazivá mlha : Freezing fog 263: Nepravidelné slabé mrholení : Patchy light drizzle 266: Slabé mrholení : Light drizzle 281: Mrazivé mrholení : Freezing drizzle 284: Silné mrazivé mrholení : Heavy freezing drizzle 293: Nepravidelný slabý déšť : Patchy light rain 296: Slabý déšť : Light rain 299: Občasný déšť : Moderate rain at times 302: Déšť : Moderate rain 305: Občasný hustý déšť : Heavy rain at times 308: Silný déšť : Heavy rain 311: Slabý mrazivý déšť : Light freezing rain 314: Smíšený mrazivý déšť : Moderate or heavy freezing rain 317: Slabé kroupění : Light sleet 320: Smíšené kroupění : Moderate or heavy sleet 323: Nepravidelné slabé sněžení : Patchy light snow 326: Slabé sněžení : Light snow 329: Nepravidelné sněžení : Patchy moderate snow 332: Sněžení : Moderate snow 335: Nepravidelné husté sněžení : Patchy heavy snow 338: Silné sněžení : Heavy snow 350: Kroupy : Ice pellets 353: Lehká déšťová sprcha : Light rain shower 356: Smíšený déšťová sprcha : Moderate or heavy rain shower 359: Přívalová déšťová sprcha : Torrential rain shower 362: Slabé přeháňková sprcha : Light sleet showers 365: Silné přeháňková sprcha : Moderate or heavy sleet showers 368: Slabé sprcha se sněžením : Light snow showers 371: Silné sprcha se sněžením : Moderate or heavy snow showers 386: Nepravidelná slabá bouřka : Patchy light rain with thunder 389: Silná bouřka : Moderate or heavy rain with thunder 392: Nepravidelný slabá bouřka se sněžením : Patchy light snow with thunder 395: Silné sněžení s bouřkou : Moderate or heavy snow with thunder ================================================ FILE: share/translations/cy.txt ================================================ 113: Glir : Clear 113: Heulog : Sunny 116: Rhannol gymylog : Partly cloudy 119: Cymylog : Cloudy 122: Cymylog : Overcast 143: Niwlog : Mist 176: Glaw anghyson yn bosib : Patchy rain possible 179: Eira anghyson yn bosib : Patchy snow possible 182: Eirlaw anghyson yn bosib : Patchy sleet possible 185: Rhewi a glaw mân anghyson yn bosib : Patchy freezing drizzle possible 200: Mellt yn bosib : Thundery outbreaks possible 227: Eira yn cael ei chwythu : Blowing snow 230: Storm eira : Blizzard 248: Niwl : Fog 260: Niwl rhewllyd : Freezing fog 263: Glaw mân ysgafn anghyson : Patchy light drizzle 266: Glaw mân ysgafn : Light drizzle 281: Glaw mân rhewllyd : Freezing drizzle 284: Glaw mân rhewllyd trwm : Heavy freezing drizzle 293: Glaw ysgafn anghyson : Patchy light rain 296: Glaw ysgafn : Light rain 299: Glaw cymedrol ar adegau : Moderate rain at times 302: Glaw cymedrol : Moderate rain 305: Glaw trwm ar adegau : Heavy rain at times 308: Glaw trwm : Heavy rain 311: Glaw rhewllyd ysgafn : Light freezing rain 314: Glaw rhewllyd trwm neu cymedrol : Moderate or heavy freezing rain 317: Eirlaw ysgafn : Light sleet 320: Eirlaw cymedrol neu trwn : Moderate or heavy sleet 323: Eira ysgafn anghyson : Patchy light snow 326: Eira ysgafn : Light snow 329: Eira cymedrol anghyson : Patchy moderate snow 332: Eira cymedrol : Moderate snow 335: Eira trwm anghyson : Patchy heavy snow 338: Eira trwm : Heavy snow 350: Pelenni iâ : Ice pellets 353: Cawod o law ysgafn : Light rain shower 356: Cawod o law cymedrol neu trwm : Moderate or heavy rain shower 359: Cawod o law trwm : Torrential rain shower 362: Cawodydd o eirlaw ysgafn : Light sleet showers 365: Cawodydd o eirlaw cymedrol new trwm: Moderate or heavy sleet showers 368: Cawodydd o eira ysgafn : Light snow showers 368: Cawod o law ac eira ysgafn, Niwl : Light Rain And Snow Shower, Mist 371: Cawodydd o eira cymedrol neu trwm : Moderate or heavy snow showers 386: Glaw ysgafn anghyson gyda mellt : Patchy light rain with thunder 389: Glaw cymedrol neu trwm gyda mellt : Moderate or heavy rain with thunder 392: Eira ysgafn anghyson gyda mellt : Patchy light snow with thunder 395: Eira cymedrol neu trwm gyda mellt : Moderate or heavy snow with thunder ================================================ FILE: share/translations/da-help.txt ================================================ Brugsanvisning: $ curl wttr.in # Nuværende lokation $ curl wttr.in/aarhus # Vejret i Aarhus Understøttede lokationsspecifikationer: /Skanderborg # Bynavn /~Aarhus+stadion # Valgfri lokation /Москва # Unicode-navn på lokation på alle sprog /cph # Flyvepladskode (3 bogstaver) /@stackoverflow.com # Domænenavn /94107 # Postnummer (kun USA) /-78.46,106.79 # GPS-koordinater Særlige lokationer: /moon # Månefase (brug med ,+US eller ,+France for disse lokationer) /moon@2014-10-26 # Månefase for specifik dato (@2014-10-26) Enheder: ?m # Metrisk (SI) (standard alle steder undtagen i USA) ?u # USCS (standard i USA) ?M # Vindstyrke i meter per sekund Visningsvalg: ?0 # Øjebliksvejr ?1 # Øjebliksvejr + 1 dag ?2 # Øjebliksvejr + 2 dage ?n # Simplificeret visning (kun dag og nat) ?q # Begrænset visning (ingen "Vejrmelding"-tekst) ?Q # Meget begrænset visning (ingen "Vejrmelding", ingen bynavn) ?T # Ingen terminalsekvenser (ingen farver) PNG valg: /paris.png # Generér en PNG-fil ?p # Tilføj ramme ?t # Sæt gennemsigtighed til 150 transparency=... # Tilpas gennemsigtighed fra 0 til 255 (255 = ikke gennemsigtigt) Kombinationsmuligheder: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # For .PNG er fil-modus specificeret efter _ /Rome_0pq_lang=it.png # Sprog defineres efter underscore (_) Oversættelser: $ curl da.wttr.in/Aarhus $ curl wttr.in/aarhus?lang=da $ curl -H "Accept-Language: da" wttr.in/aarhus Understøttede sprog: FULL_TRANSLATION (understøttet) PARTIAL_TRANSLATION (under udarbejdelse) Særlige URL'er: /:help # Vis denne side /:bash.function # Vis den foreslåede bash-funktion wttr() /:translation # Vis information om oversætterne ================================================ FILE: share/translations/da.txt ================================================ 113: Skyfrit : Clear 113: Sol : Sunny 116: Delvist skyet : Partly cloudy 119: Skyet : Cloudy 122: Overskyet : Overcast 143: Diset : Mist 176: Skiftende regn : Patchy rain possible 179: Skiftende sne : Patchy snow possible 182: Skiftende slud : Patchy sleet possible 185: Skiftende kold småregn : Patchy freezing drizzle possible 200: Fare for torden : Thundery outbreaks possible 227: Snefygning : Blowing snow 230: Snestorm : Blizzard 248: Tåge : Fog 260: Frysende tåge : Freezing fog 263: Skiftende og let støvregn : Patchy light drizzle 266: Let støvregn : Light drizzle 281: Kold støvregn : Freezing drizzle 284: Kraftig kold regn : Heavy freezing drizzle 293: Skiftende let regn : Patchy light rain 296: Let regn : Light rain 299: Skiftevist en smule regn : Moderate rain at times 302: En smule regn : Moderate rain 305: Skiftevist kraftig regn : Heavy rain at times 308: Kraftig regn : Heavy rain 311: Let og frysende regn : Light freezing rain 314: Moderat eller kraftig frysende regn : Moderate or heavy freezing rain 317: Let slud : Light sleet 320: Moderat eller kraftig slud : Moderate or heavy sleet 323: Byger af let sne : Patchy light snow 326: Let sne : Light snow 329: Byger med moderat sne : Patchy moderate snow 332: Moderat sne : Moderate snow 335: Byger med kraftig sne : Patchy heavy snow 338: Kraftig sne : Heavy snow 350: Haglbyger : Ice pellets 353: Lette regnbyger : Light rain shower 356: Moderate eller kraftige regnbyger : Moderate or heavy rain shower 359: Skybrud : Torrential rain shower 362: Lette sludbyger : Light sleet showers 365: Moderate eller kraftige sludbyger : Moderate or heavy sleet showers 368: Lette snebyger : Light snow showers 371: Moderate eller kraftige snebyger : Moderate or heavy snow showers 386: Byger af let regn med torden : Patchy light rain with thunder 389: Moderat eller krafig regn med torden : Moderate or heavy rain with thunder 392: Byger af let sne med torden : Patchy light snow with thunder 395: Moderat eller kraftig sne med torden : Moderate or heavy snow with thunder ================================================ FILE: share/translations/de-help.txt ================================================ Benutzung: $ curl wttr.in # aktuelle Position $ curl wttr.in/muc # Wetter, Flughafen München Unterstütze Ortstypen: /paris # Stadtname /~Eiffel+tower # wählbarer Ort /Москва # Unicode Name von einem Ort in irgendeiner Sprache /muc # IATA-Flughafencode (3 Buchstaben) /@stackoverflow.com # Domainname /94107 # Area code (nur für USA) /-78.46,106.79 # GPS Koordinaten Spezielle Orte: /moon # Mondphase (bei Benutzung von z.B. ,+US oder ,+France wird die Phase des jeweiligen Ortes angezeigt) /moon@2016-10-25 # Mondphase eines Tages (@2016-10-25) Maßeinheiten: ?m # metrisch (SI) (standard überall außer bei Orten in den USA) ?u # USCS (standard in den USA) ?M # Windgeschwindigkeiten in m/s Ansichteinstellungen: ?0 # Zeige nur aktuelles Wetter ?1 # Zeige aktuelles Wetter + 1 Tag ?2 # Zeige aktuelles Wetter + 2 Tage ?n # Kleine Version (nur Tag & Nacht) ?q # Schmale Version (kein 'Wetter Report' Text) ?Q # Superschmale Version (kein 'Wetter Report' Text und Ortsname) ?T # Keine Farben PNG optionen: /paris.png # generiert eine PNG Datei ?p # fügt einen Rahmen hinzu ?t # Transparenz von 150 transparency=... # Transparenz von 0 bis 255 (255 = nicht transparent) Optionen können kombiniert werden: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # wird eine PNG benutzt, dann werden diese Optionen nach einem _ (Unterstrich) hinzugefügt /Rome_0pq_lang=it.png # einzelne Optionen werden mit einem _ (Unterstrich) getrennt Lokalisierung: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Unterstützte Sprachen: FULL_TRANSLATION (supported) PARTIAL_TRANSLATION (in progress) Spezialseiten: /:help # zeigt diese Seite an /:bash.function # zeigt empfehlenswerte bash Funktion wttr() an /:translation # zeigt Informationen der Übersetzer an ================================================ FILE: share/translations/de.txt ================================================ : Dunst in der Nähe : Haze in vicinity : Starker Regen- und Schneeschauer : Heavy rain and snow shower : Starker Regenschauer : Heavy rain shower : Leichter Nieselregen und Schneegriesel : Light drizzle and snow grains : Leichter Regen- und Nebelschauer : Light rain and mist shower : Leichter Hagel-/Schneegraupelschauer : Light small hail/snow pellets shower : Leichter Schauer unbekannter Niederschlag : Light unknown precipitation shower : Regen und leichter Hagel/Schneegraupel mit Gewitter : Rain and small hail/snow pellets with thunderstorm : Schneeschauer in der Nähe : Snow shower in vicinity : Gewitterausbrüche in der Nähe : Thundery outbreaks nearby : Bodennahe Schneeverwehung : Low drifting snow : Nebelfelder : Patches of fog : Nieselregen : Drizzle : Leichter Nieselregen und Regen : Light drizzle and rain : Schnee : Snow : Regen : Rain : Regenschauer : Rain Shower : Sandverwehungen : Blowing sand : Weitläufige Sandverwehungen : Blowing widespread dust : Weitläufige Sandverwehungen in der Nähe : Blowing widespread dust in vicinity : Nieselregen und Nebel : Drizzle and fog : Nieselregen und Regen : Drizzle and rain : Nieselregen und Regenschauer : Drizzle and rain shower : Nieselregen mit Gewitter : Drizzle with thunderstorm : Sandsturm : Dust storm : Nebel in der Nähe : Fog in vicinity : Gefrierender Nieselregen und Regen : Freezing drizzle and rain : Gefrierender Nebel : Freezing mist : Gefrierender Regen : Freezing rain : Gefrierender Regen und Schnee : Freezing rain and snow : Gefrierender unbekannter Niederschlag : Freezing unknown precipitation : Trichterwolke in der Nähe : Funnel cloud in vicinity : Hagelschauer : Hail shower : Hagel mit Gewitter : Hail with thunderstorm : Dunst : Haze : Starke Schneeverwehungen : Heavy blowing snow : Starker Nieselregen : Heavy drizzle : Starker Nieselregen und Regen : Heavy drizzle and rain : Starker gefrierender unbekannter Niederschlag : Heavy freezing unknown precipitation : Starker Hagel mit Gewitter : Heavy hail with thunderstorm : Starke Eiskörner : Heavy ice pallets : Starker Regen und Hagelschauer : Heavy rain and hail shower : Starker Regen und Hagelschauer mit Gewitter : Heavy rain and hail with thunderstorm : Starker Regen und kleiner Hagelschauer : Heavy rain and small hail/snow pallets shower : Starker Regen und kleiner Hagel mit Gewitter : Heavy rain and small hail/snow pallets with thunderstorm : Starker Regen und Schnee : Heavy rain and snow : Starker Regen und Schnee sowie kleiner Hagel mit Gewitter : Heavy rain and snow and small hail/snow pallets with thunderstorm : Starker Regen mit Gewitter : Heavy rain with thunderstorm : Starker kleiner Hagelschauer : Heavy small hail/snow pallets shower : Starker kleiner Hagel mit Gewitter : Heavy small hail/snow pallets with thunderstorm : Starker Schnee und kleiner Hagelschauer : Heavy snow and small hail/snow pallets shower : Starker Schnee und kleiner Hagel mit Gewitter : Heavy snow and small hail/snow pallets with thunderstorm : Starker Schneeschauer : Heavy snow shower : Starker Schnee mit Gewitter : Heavy snow with thunderstorm : Eiskristalle : Ice crystals : Eiskörner : Ice pallets : Leichte Schneeverwehungen : Light blowing snow : Leichter Nieselregen und Nebel : Light drizzle and fog : Leichter Nieselregen und Eiskörner : Light drizzle and ice pallets : Leichter Nieselregen und Regen und Schnee : Light drizzle and rain and snow : Leichter Nieselregen und Regenschauer : Light drizzle and rain shower : Leichter Nieselregen und Regen mit Gewitter : Light drizzle and rain with thunderstorm : Leichter Nieselregen und Schnee : Light drizzle and snow : Leichter Nieselregen mit Gewitter : Light drizzle with thunderstorm : Leichter gefrierender Nieselregen : Light freezing drizzle : Leichter gefrierender Nieselregen und Regen : Light freezing drizzle and rain : Leichter gefrierender Nieselregen und Schnee : Light freezing drizzle and snow : Leichter gefrierender Nebel : Light freezing fog : Leichter gefrierender Regen und Schnee : Light freezing rain and snow : Leichter gefrierender Schneegraupel und Nebel : Light freezing snow grains and fog : Leichter gefrierender unbekannter Niederschlag : Light freezing unknown precipitation : Leichter Hagelschauer : Light hail shower : Leichte Eiskristalle : Light ice crystals : Leichte Eiskörner : Light ice pallets : Leichte bodennahe Verwehungen : Light low drifting : Leichte bodennahe Schneeverwehungen : Light low drifting snow : Leichter Regen und Hagelschauer : Light rain and hail shower : Leichter Regen und Hagel mit Gewitter : Light rain and hail with thunderstorm : Leichter Regen und Eiskörner : Light rain and ice pallets : Leichter Regen und Nebel : Light rain and mist : Leichter Regen und kleiner Hagelschauer : Light rain and small hail/snow pallets shower : Leichter Regen und kleiner Hagel mit Gewitter : Light rain and small hail/snow pallets with thunderstorm : Leichter Regen und Schnee : Light rain and snow : Leichter Regen und Schnee sowie kleiner Hagelschauer : Light rain and snow and small hail/snow pallets shower : Leichter Regen und Schneegraupel : Light rain and snow grains : Leichter Regen und Schneeschauer : Light rain and snow shower : Leichter Regen und Schnee mit Gewitter : Light rain and snow with thunderstorm : Leichter Regen mit Gewitter : Light rain with thunderstorm : Leichter Schauer : Light shower : Leichter Hagelschauer : Light showers of ice pellets : Leichter kleiner Hagel : Light small hail/snow pallets : Leichter kleiner Hagelschauer : Light small hail/snow pallets shower : Leichter kleiner Hagel mit Gewitter : Light small hail/snow pallets with thunderstorm : Leichter Schnee und Eiskörner : Light snow and ice pallets : Leichter Schnee und kleiner Hagel : Light snow and small hail/snow pallets : Leichter Schneeschauer mit kleinem Hagel : Light snow and small hail/snow pallets shower : Leichter Schnee mit kleinem Hagel und Gewitter : Light snow and small hail/snow pallets with thunderstorm : Leichter Schnee und Schneegraupel : Light snow and snow grains : Leichtes Schneegraupel : Light snow grains : Leichter Schneeschauer : Light snow shower : Leichter Schnee mit Gewitter : Light snow with thunderstorm : Leichtes Gewitter : Light thunderstorm : Leichter unbekannter Niederschlag : Light unknown precipitation : Leicht ausgeprägte Staub- oder Sandwirbel : Light well-developed dust/sand whirls : Bodennaher verwehender Sand : Low drifting sand : Bodennaher verwehender Staub : Low drifting widespread dust : Nebel mit Gewitter : Mist with thunderstorm : Mäßig bis starker Regen mit Gewitter : Moderate or heavy rain in area with thunder : Mäßig bis starker Schnee mit Gewitter : Moderate or heavy snow in area with thunder : Teilweise Nebel : Partial fog : Nebelfelder in der Nähe : Patches of fog in vicinity : Ortsweise gefrierender Nieselregen : Patchy freezing drizzle nearby : Gebietsweise leichter Regen mit Gewitter : Patchy light rain in area with thunder : Gebietsweise leichter Schnee mit Gewitter : Patchy light snow in area with thunder : Örtlich Regen : Patchy rain nearby : Örtlich Schneeregen : Patchy sleet nearby : Örtlich Schnee : Patchy snow nearby : Regen und Hagelschauer : Rain and hail shower : Regen und Hagel mit Gewitter : Rain and hail with thunderstorm : Regen und Nebel : Rain and mist : Regen und Nebel mit Gewitter : Rain and mist with thunderstorm : Regen und kleiner Hagelschauer : Rain and small hail/snow pallets shower : Regen und kleiner Hagel mit Gewitter : Rain and small hail/snow pallets with thunderstorm : Regen und Schnee : Rain and snow : Regen und Schnee sowie kleiner Hagelschauer : Rain and snow and small hail/snow pallets shower : Regen und Schneegraupel : Rain and snow grains : Regen und Schneeschauer : Rain and snow shower : Regen und Schnee mit Gewitter : Rain and snow with thunderstorm : Regen in der Nähe : Rain in vicinity : Regenschauer in der Nähe : Rain shower in vicinity : Regen mit Gewitter : Rain with thunderstorm : Sand : Sand : Sandsturm : Sandstorm : Seichter Nebel : Shallow fog : Seichter Dunst : Shallow mist : Schauer : Shower : Schauer in der Nähe : Shower in vicinity : Leichter Hagel : Small hail/snow pallets : Leichter Hagelschauer : Small hail/snow pallets shower : Leichter Hagel mit Gewitter : Small hail/snow pallets with thunderstorm : Rauch : Smoke : Schnee und Eiskörner : Snow and ice pallets : Schnee und Nebel : Snow and mist : Schnee- und leichte Hagelschauer : Snow and small hail/snow pellets shower : Schnee- und leichte Hagelschauer mit Gewitter : Snow and small hail/snow pallets with thunderstorm : Schnee und Schneegraupel : Snow and snow grains : Schneegraupel : Snow grains : Schneeschauer : Snow shower : Schnee mit Gewitter : Snow with thunderstorm : Gewitterböen : Squalls : Gewitter : Thunderstorm : Gewitter in der Nähe : Thunderstorm in vicinity : Gewitter in der Nähe : Thundery outbreaks in nearby : Unbekannter Niederschlag : Unknown precipitation : Schauer mit unbekanntem Niederschlag : Unknown precipitation shower : Unbekannter Niederschlag mit Gewitter : Unknown precipitation with thunderstorm : Vulkanasche : Volcanic ash : Vulkanasche in der Nähe : Volcanic ash in vicinity : Stark ausgeprägte Staub-/Sandwirbel in der Nähe : Well-developed dust/sand whirls in vicinity : flächendeckender Staub : Widespread dust 113: Wolkenlos : Clear 113: Sonnig : Sunny 116: Leicht Bewölkt : Partly cloudy 119: Wolkig : Cloudy 122: Bedeckt : Overcast 143: Nebel : Mist 176: Stellenweise Regen möglich : Patchy rain possible 179: Stellenweise Schnee möglich : Patchy snow possible 182: Stellenweise Schneeregen möglich : Patchy sleet possible 185: Stellenweise gefrierende Nässe möglich : Patchy freezing drizzle possible 200: Gewitter möglich : Thundery outbreaks possible 227: Schneesturm : Blowing snow 230: Blizzard : Blizzard 248: Nebel : Fog 260: Gefrierender Nebel : Freezing fog 263: Stellenweise Nieselregen : Patchy light drizzle 266: Leichter Nieselregen : Light drizzle 281: Gefrierender Nieselregen : Freezing drizzle 284: Starker gefrierender Nieselregen : Heavy freezing drizzle 293: Stellenweise leichter Regen : Patchy light rain 296: Leichter Regen : Light rain 299: Stellenweise gemäßigter Regen : Moderate rain at times 302: Gemäßigter Regen : Moderate rain 305: Stellenweise starker Regen : Heavy rain at times 308: Starker Regen : Heavy rain 311: Leichter gefrierender Regen : Light freezing rain 314: Gemäßigter oder starker gefrierender Regen : Moderate or heavy freezing rain 317: Leichter Schneeregen : Light sleet 320: Gemäßigter oder starker Schneeregen : Moderate or heavy sleet 323: Stellenweise leichter Schneefall : Patchy light snow 326: Leichter Schneefall : Light snow 329: Stellenweise gemäßigter Schneefall : Patchy moderate snow 332: Gemäßigter Schneefall : Moderate snow 335: Stellenweise starker Schneefall : Patchy heavy snow 338: Starker Schneefall : Heavy snow 350: Eiskörner : Ice pellets 353: Leichter Regen : Light rain shower 356: Gemäßigter oder starker Regen : Moderate or heavy rain shower 359: Wolkenbruch : Torrential rain shower 362: Leichter Schneeregen : Light sleet showers 365: Gemäßigter oder starker Schneeregen : Moderate or heavy sleet showers 368: Leichter Schneefall : Light snow showers 371: Gemäßigter oder starker Schneefall : Moderate or heavy snow showers 386: Stellenweise leichter Regen mit Gewitter : Patchy light rain with thunder 389: Gemäßigter oder starker Regen mit Gewitter : Moderate or heavy rain with thunder 392: Stellenweise leichter Schneefall mit Gewitter : Patchy light snow with thunder 395: Gemäßigter oder starker Schneefall mit Gewitter : Moderate or heavy snow with thunder ================================================ FILE: share/translations/dk-help.txt ================================================ Brugsanvisning: $ curl wttr.in # nuværende lokation $ curl wttr.in/osl # vejret på Gardermoen flyveplads Understøttede lokationsspecifikationer: /gistrup # bynavn /~Aalborg+stadion # valgfri lokation /Москва # Unicode navn på lokation på alle sprog /cph # flyvepladskode (3 bogstaver) /@stackoverflow.com # domønenavn /94107 # postnummer (kun USA) /-78.46,106.79 # GPS-koordinater Specielle lokationer: /moon # Månefase (brug med ,+US eller ,+France for disse lokationer) /moon@2014-10-26 # Månefase for specifik dato (@2014-10-26) Enheder: ?m # metrisk (SI) (standard alle steder undtaget i USA) ?u # USCS (standard i USA) ?M # vindstyrke i meter per sekund Visningsvalg: ?0 # kun vejret nu ?1 # vejret nu + 1 dag ?2 # vejret nu + 2 dage ?n # smal visning (kun dag og nat) ?q # stille visning (ingen "Vejrmelding"-tekst) ?Q # superstille visning (ingen "Vejrmelding", ingen bynavn) ?T # ingen terminalsekvenser (ingen farver) PNG valg: /paris.png # generer en PNG-fil ?p # tegn ramme på ?t # gennemsigtighed 150 transparency=... # gennemsigtighed fra 0 til 255 (255 = ikke gennemsigtigt) Tilvalg kan kombineres: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # for PNG er filmodus specificeret efter _ /Rome_0pq_lang=it.png # lange tilvalg separeres med underscore (_) Oversættelser: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Understøttede sprog: FULL_TRANSLATION (understøttet) PARTIAL_TRANSLATION (under udarbejdning) Specielle URLer: /:help # vis denne side /:bash.function # vis den foreslåede bash-funktion wttr() /:translation # vis information om oversætterne ================================================ FILE: share/translations/el-help.txt ================================================ Χρήση: $ curl wttr.in # καιρός τρέχουσας τοποθεσίας (κατά προσέγγιση, βάσει IP) $ curl wttr.in/ath # ο καιρός στο Διεθνή Αερολιμένα Αθηνών «Ελ. Βενιζέλος» Υποστηριζόμενες μορφές τοποθεσιών: /paris # όνομα πόλης /~Eiffel+tower # κάποια τοποθεσία /Москва # Unicode όνομα οποιασδήποτε τοποθεσίας σε οποιαδήποτε γλώσσα /ath # κωδικός αεροδρομίου (3 γράμματα) /@stackoverflow.com # (διαδικτυακό) όνομα τομέα (domain) /94107 # κωδικός περιοχής /-78.46,106.79 # γεωγραφικές συντεταγμένες (GPS) Εξειδικευμένες τοποθεσίες: /moon # Φάση Σελήνης (προσθέστε ,+US ή ,+France για τις πόλεις) /moon@2016-10-25 # Φάση Σελήνης για την ημερομηνία (@2016-10-25) /moon@2019-06-18.png?T -omoon.png # Φάση Σελήνης για την ημερομηνία (@2019-06-18), όχι ANSI, εξοδος στο αρχείο `moon.png` Σύστημα μέτρησης: m # μετρικό (SI) (προεπιλογή για οπουδήποτε εκτός ΗΠΑ) u # USCS (προεπιλογή για τις ΗΠΑ) M # εμφάνιση ταχύτητας ανέμου σε m/s Επιλογές Προβολής: 0 # καιρός τρέχουσας ημέρας μόνο 1 # καιρός τρέχουσας ημέρας + 1 ημέρα 2 # καιρός τρέχουσας ημέρας + 2 ημέρες A # παραβλέπει τον User-Agent και επιβάλει μορφή εξόδου ANSI (τερματικό) F # μη εμφάνιση της γραμμής "Follow" n # συνοπτική εκδοχή (ημέρα και νύχτα μόνο) q # σιωπηλή εκδοχή (όχι κείμενο "Weather report") Q # υπερσιωπηλή εκδοχή (όχι κείμενο "Weather report", όχι όνομα πόλης) T # απενεργοποίηση ακολουθιών τερματικού (όχι χρώματα) Επιλογές PNG: /paris.png # δημιουργία PNG αρχείου p # προσθήκη πλαισίου γύρω από τα περιέχομενα εξόδου t # διαφάνεια 150 transparency=nnn # διαφάνεια nnn (από 0 έως 255 - 255 = αδιαφανές) Οι επιλογές μπορούν να συνδυαστούν: /Paris?0pq /Paris?0pq&lang=fr /Paris?_0pq.png # η έξοδος σε PNG αρχείο ορίζεται μετά από το _ /Rome?_0pq_lang=it.png # μακροσκελείς επιλογές διαχωριζόμενες με κάτω παύλα (underscore) /Athens?"1pTng&lang=el" # συνδυασμένες επιλογές μπορούν να περικλείονται σε εισαγωγικά! Τοπικοποίηση: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Υποστηριζόμενες γλώσσες: FULL_TRANSLATION (πλήρως υποστηριζόμενες) PARTIAL_TRANSLATION (εργασία σε εξέλιξη) Ειδικά URLs: /:help # εμφάνιση της παρούσας βοήθειας /:bash.function # εμφάνιση συνιστώμενων bash λειτουργιών wttr() /:translation # εμφάνιση πληροφοριών για τους μεταφραστές ================================================ FILE: share/translations/el.txt ================================================ 113: Καθαρός : Clear 113: Λιακάδα : Sunny 116: Αραιή συννεφιά : Partly cloudy 119: Συννεφιά : Cloudy 122: Πυκνή νέφωση : Overcast 143: Αραιή ομίχλη : Mist 176: Πιθανή βροχή τοπικά : Patchy rain possible 179: Πιθανή χιονόπτωση τοπικά : Patchy snow possible 182: Πιθανή χαλαζόπτωση τοπικά : Patchy sleet possible 185: Πιθανό παγωμ. ψιλόβροχο τοπικά : Patchy freezing drizzle possible 200: Πιθανή πτώση κεραυνών : Thundery outbreaks possible 227: Ριπές χιονιού : Blowing snow 230: Χιονοθύελλα : Blizzard 248: Ομίχλη : Fog 260: Παγωμένη ομίχλη : Freezing fog 263: Ασθενής ψιχάλα τοπικά : Patchy light drizzle 266: Ασθενής ψιχάλα : Light drizzle 281: Παγωμένο ψιλόβροχο : Freezing drizzle 284: Πυκνό παγωμένο ψιλόβροχο : Heavy freezing drizzle 293: Ασθενής όμβρος τοπικά : Patchy light rain 296: Ασθενής βροχόπτωση : Light rain 299: Μέτρια βροχή παροδικά : Moderate rain at times 302: Μέτρια βροχόπτωση : Moderate rain 305: Έντονη βροχή παροδικά : Heavy rain at times 308: Έντονη βροχόπτωση : Heavy rain 311: Αραιό χιονόνερο : Light freezing rain 314: Μέτριο ή έντονο χιονόνερο : Moderate or heavy freezing rain 317: Ασθενής χαλαζόπτωση : Light sleet 320: Μέτρια ή έντονη χαλαζόπτωση : Moderate or heavy sleet 323: Αραιή χιονόπτωση κατά τόπους : Patchy light snow 326: Αραιή χιονόπτωση : Light snow 329: Μέτρια χιονόπτωση τοπικά : Patchy moderate snow 332: Μέτρια χιονόπτωση : Moderate snow 335: Πυκνή χιονόπτωση τοπικά : Patchy heavy snow 338: Πυκνή χιονόπτωση : Heavy snow 350: Χιονόνερο : Ice pellets 353: Ασθενής βροχή παροδικά : Light rain shower 356: Μέτρια ή έντονη βροχή παροδικά : Moderate or heavy rain shower 359: Καταρρακτώδης βροχή παροδικά : Torrential rain shower 362: Ασθενής χαλαζόπτωση παροδικά : Light sleet showers 365: Μέτρια/έντονη χαλαζόπτωση παροδικά : Moderate or heavy sleet showers 368: Αραιή χιονόπτωση παροδικά : Light snow showers 371: Μέτρια ή πυκνή χιονόπτωση παροδικά : Moderate or heavy snow showers 386: Ασθενής βροχή με κεραυνούς τοπικά : Patchy light rain with thunder 389: Μέτρια ή δυνατή βροχή με κεραυνούς : Moderate or heavy rain with thunder 392: Ασθενής χιονόπτωση-κεραυνοί τοπικά : Patchy light snow with thunder 395: Μέτρια ή πυκνή χιονόπτωση-κεραυνοί : Moderate or heavy snow with thunder ================================================ FILE: share/translations/en.txt ================================================ 113: : Clear 113: : Sunny 116: : Partly cloudy 119: : Cloudy 122: : Overcast 143: : Mist 176: : Patchy rain possible 179: : Patchy snow possible 182: : Patchy sleet possible 185: : Patchy freezing drizzle possible 200: : Thundery outbreaks possible 227: : Blowing snow 230: : Blizzard 248: : Fog 260: : Freezing fog 263: : Patchy light drizzle 266: : Light drizzle 281: : Freezing drizzle 284: : Heavy freezing drizzle 293: : Patchy light rain 296: : Light rain 299: : Moderate rain at times 302: : Moderate rain 305: : Heavy rain at times 308: : Heavy rain 311: : Light freezing rain 314: : Moderate or heavy freezing rain 317: : Light sleet 320: : Moderate or heavy sleet 323: : Patchy light snow 326: : Light snow 329: : Patchy moderate snow 332: : Moderate snow 335: : Patchy heavy snow 338: : Heavy snow 350: : Ice pellets 353: : Light rain shower 356: : Moderate or heavy rain shower 359: : Torrential rain shower 362: : Light sleet showers 365: : Moderate or heavy sleet showers 368: : Light snow showers 371: : Moderate or heavy snow showers 386: : Patchy light rain with thunder 389: : Moderate or heavy rain with thunder 392: : Patchy light snow with thunder 395: : Moderate or heavy snow with thunder ================================================ FILE: share/translations/eo.txt ================================================ 113: Klara : Clear 113: Suna : Sunny 116: Parte nuba : Partly cloudy 119: Nuba : Cloudy 122: Sensuna : Overcast 143: Nebulo : Mist 176: Flikeca pluvo ebla : Patchy rain possible 179: Flikeca neĝo ebla : Patchy snow possible 182: Flikeca hajlneĝo ebla : Patchy sleet possible 185: Flikeca frosta drizelo ebla : Patchy freezing drizzle possible 200: Tondro ebla : Thundery outbreaks possible 227: Blova neĝo : Blowing snow 230: Blizardo : Blizzard 248: Nebulo : Fog 260: Frosta nebulo : Freezing fog 263: Flikeca malforta drizelo : Patchy light drizzle 266: Malforta drizelo : Light drizzle 281: Frosta drizelo : Freezing drizzle 284: Forta frosta drizelo : Heavy freezing drizzle 293: Flikeca forta pluvo : Patchy light rain 296: Malforta pluvo : Light rain 299: Modera pluvo foje : Moderate rain at times 302: Modera pluvo : Moderate rain 305: Forta pluvvo foje : Heavy rain at times 308: Forta pluvo : Heavy rain 311: Malforta glacipluvo : Light freezing rain 314: Modera aŭ forta glacipluvo : Moderate or heavy freezing rain 317: Malforta grajlo : Light sleet 320: Modera aŭ forta hajlneĝo : Moderate or heavy sleet 323: Flikeca malforta neĝo : Patchy light snow 326: Malforta neĝo : Light snow 329: Flikeca modera neĝo : Patchy moderate snow 332: Modera neĝo : Moderate snow 335: Flikeca forta neĝo : Patchy heavy snow 338: Forta neĝo : Heavy snow 350: Grajlo : Ice pellets 353: Malforta pluvoverŝo : Light rain shower 356: Modera pluvoverŝo : Moderate or heavy rain shower 359: Torrenta pluvoverŝo : Torrential rain shower 362: Malforta glacipluvego : Light sleet showers 365: Modera aŭ forta glacipluvego : Moderate or heavy sleet showers 368: Malforta hajlneĝo : Light snow showers 371: Modera aŭ forta neĝipluvego : Moderate or heavy snow showers 386: Flikeca malforta pluvo kun tondro : Patchy light rain with thunder 389: Modera aŭ forta pluvo kun tondro : Moderate or heavy rain with thunder 392: Flikeca malforta neĝo kun tondro : Patchy light snow with thunder 395: Modera aŭ forta neĝo kun tondro : Moderate or heavy snow with thunder ================================================ FILE: share/translations/es-help.txt ================================================ Instrucciones: $ curl wttr.in # El clima en su ubicación actual $ curl wttr.in/muc # El clima en el aeropuerto de Múnich Tipos de ubicación soportados: /paris # El nombre de una ciudad /~Eiffel+tower # El nombre de cualquier sitio famoso /Москва # El nombre en Unicode de cualquier sitio en cualquier idioma /muc # El código de un aeropuerto (3 letras) /@stackoverflow.com # El nombre de un dominio web /94107 # Un código de area /-78.46,106.79 # Coordenadas de GPS Sitios especiales: /moon # La fase de la luna (añade ,+US o ,+France para estos paises) /moon@2016-10-25 # La fase de la luna en una fecha específica (@2016-10-25) Unidades: ?m # Métricas (SI) (el estándar en todos los sitios excepto en EEUU) ?u # Sistema Unificado de Clasificación del Suelo o USCS (el estándar en EEUU) ?M # Mostrar la velocidad del viento en m/s Opciones de visualización: ?0 # Solamente el clima actual ?1 # El clima actual + la previsión del día siguiente ?2 # El clima actual + la previsión de los dos días siguientes ?A # ignora al agente del usuario y fuerza el formato de salida ANSI (terminal) ?F # No muestra la linea de "Seguir" ?n # Versión corta (solo día y noche) ?q # Versión silenciosa (sin el texto de "El tiempo en") ?Q # Versión supersilenciosa (ni "El tiempo en" ni el nombre de la ciudad) ?T # Desactiva las secuencias de terminal (sin colores) Opciones de PNG: /paris.png # Genera una imagen PNG ?p # Añade un borde alrededor de la imagen ?t # Transparencia = 150 transparency=... # Transparencia de 0 a 255 (255 = sin transparencia) Las opciones se pueden utilizar conjuntamente: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # En modo PNG las opciones se especifican después del carácter _ (guión bajo) /Rome_0pq_lang=it.png # Una larga secuencia de opciones se puede separar por el carácter _ (guión bajo) Ubicación: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Idiomas soportados: FULL_TRANSLATION (soportados) PARTIAL_TRANSLATION (en progreso) URLs especiales: /:help # Muestra esta página /:bash.function # Muestra una función wttr() sugerida en bash /:translation # Muestra información acerca de los traductores ================================================ FILE: share/translations/es.txt ================================================ 114: Despejado : Clear : 113: Soleado : Sunny : 116: Parcialmente nublado : Partly cloudy : 119: Nublado : Cloudy : 122: Cubierto de nubes : Overcast : 143: Niebla : Mist : 176: Posible lluvia irregular : Patchy rain possible : 179: Posible aguanieve irregular : Patchy snow possible : 182: Posible aguanieve irregular : Patchy sleet possible : 185: Posible llovizna helada irregular : Patchy freezing drizzle possible : 200: Posibles brotes de tormentas : Thundery outbreaks possible : 227: Nieve tormentosa : Blowing snow : 230: Ventisca : Blizzard : 248: Niebla : Fog : 260: Niebla helada : Freezing fog : 263: Llovizna ligera irregular : Patchy light drizzle : 266: Llovizna ligera : Light drizzle : 281: Llovizna helada : Freezing drizzle : 284: Llovizna muy helada : Heavy freezing drizzle : 293: Lluvia ligera irregular : Patchy light rain : 296: Lluvia ligera : Light rain : 299: Lluvia moderada ocasional : Moderate rain at times : 302: Lluvia moderada : Moderate rain : 305: Lluvia fuerte ocasional : Heavy rain at times : 308: Lluvia fuerte : Heavy rain : 311: Lluvia ligera helada : Light freezing rain : 314: Lluvia helada moderada o fuerte : Moderate or heavy freezing rain : 317: Aguanieve ligero : Light sleet : 320: Aguanieve moderado o fuerte : Moderate or heavy sleet : 323: Nieve ligera irregular : Patchy light snow : 326: Nieve ligera : Light snow : 329: Nieve moderada irregular : Patchy moderate snow : 332: Nieve moderada : Moderate snow : 335: Nieve pesada irregular : Patchy heavy snow : 338: Nieve pesada : Heavy snow : 350: Perdigones de hielo : Ice pellets : 353: Aguacero ligero : Light rain shower : 356: Aguacero moderado o fuerte : Moderate or heavy rain shower : 359: Aguacero torrencial : Torrential rain shower : 362: Aguacero ligero con aguanieve : Light sleet showers : 365: Aguacero con aguanieve moderado o fuerte : Moderate or heavy sleet showers : 368: Aguacero con nieve ligera : Light snow showers : 371: Aguacero con nieve moderada o fuerte : Moderate or heavy snow showers : 386: Lluvia ligera irregular con truenos : Patchy light rain with thunder : 389: Lluvia moderada o fuerte con truenos : Moderate or heavy rain with thunder : 392: Nevada ligera irregular con truenos : Patchy light snow with thunder : 395: Nevada moderada o fuerte con truenos : Moderate or heavy snow with thunder : : Bruma en las cercanías : Haze in vicinity : : Fuerte lluvia y nevada : Heavy rain and snow shower : : Fuerte aguacero : Heavy rain shower : : Llovizna ligera y granos de nieve : Light drizzle and snow grains : : Lluvia ligera y bruma : Light rain and mist shower : : Granizo ligero o chubasco de nieve granulada : Light small hail/snow pellets shower : : Chubasco ligero de precipitación desconocida : Light unknown precipitation shower : : Lluvia y granizo ligero o nieve granulada con tormenta : Rain and small hail/snow pellets with thunderstorm : : Chubasco de nieve en las cercanías : Snow shower in vicinity : : Brotes de tormenta en las cercanías : Thundery outbreaks nearby : : Nieve arrastrada a nivel del suelo : Low drifting snow : : Bancos de niebla : Patches of fog : : Llovizna : Drizzle : : Llovizna ligera y lluvia : Light drizzle and rain : : Nieve : Snow : : Lluvia : Rain : : Aguacero : Rain Shower : : Arena arrastrada por el viento : Blowing sand : : Polvo generalizado arrastrado por el viento : Blowing widespread dust : : Polvo generalizado arrastrado por el viento en las cercanías : Blowing widespread dust in vicinity : : Llovizna y niebla : Drizzle and fog : : Llovizna y lluvia : Drizzle and rain : : Llovizna y aguacero : Drizzle and rain shower : : Llovizna con tormenta : Drizzle with thunderstorm : : Tormenta de polvo : Dust storm : : Niebla en las cercanías : Fog in vicinity : : Llovizna helada y lluvia : Freezing drizzle and rain : : Niebla helada : Freezing mist : : Lluvia helada : Freezing rain : : Lluvia helada y nieve : Freezing rain and snow : : Precipitación desconocida helada : Freezing unknown precipitation : : Nube embudo en las cercanías : Funnel cloud in vicinity : : Chubasco de granizo : Hail shower : : Granizo con tormenta : Hail with thunderstorm : : Bruma : Haze : : Fuertes ventiscas de nieve : Heavy blowing snow : : Fuerte llovizna : Heavy drizzle : : Fuerte llovizna y lluvia : Heavy drizzle and rain : : Fuerte precipitación desconocida helada : Heavy freezing unknown precipitation : : Fuerte granizo con tormenta : Heavy hail with thunderstorm : : Fuertes gránulos de hielo : Heavy ice pallets : : Fuerte lluvia y chubasco de granizo : Heavy rain and hail shower : : Fuerte lluvia y granizo con tormenta : Heavy rain and hail with thunderstorm : : Fuerte lluvia y granizo pequeño o chubasco de nieve granulada : Heavy rain and small hail/snow pallets shower : : Fuerte lluvia y granizo pequeño o nieve granulada con tormenta : Heavy rain and small hail/snow pallets with thunderstorm : : Fuerte lluvia y nieve : Heavy rain and snow : : Fuerte lluvia y nieve junto con granizo pequeño o nieve granulada con tormenta : Heavy rain and snow and small hail/snow pallets with thunderstorm : : Fuerte lluvia con tormenta : Heavy rain with thunderstorm : : Fuerte chubasco de granizo pequeño o nieve granulada : Heavy small hail/snow pallets shower : : Fuerte granizo pequeño o nieve granulada con tormenta : Heavy small hail/snow pallets with thunderstorm : : Fuerte nieve y chubasco de granizo pequeño o nieve granulada : Heavy snow and small hail/snow pallets shower : : Fuerte nieve y granizo pequeño o nieve granulada con tormenta : Heavy snow and small hail/snow pallets with thunderstorm : : Fuerte chubasco de nieve : Heavy snow shower : : Fuerte nieve con tormenta : Heavy snow with thunderstorm : : Cristales de hielo : Ice crystals : : Gránulos de hielo : Ice pallets : : Ventiscas de nieve ligeras : Light blowing snow : : Llovizna ligera y niebla : Light drizzle and fog : : Llovizna ligera y gránulos de hielo : Light drizzle and ice pallets : : Llovizna ligera, lluvia y nieve : Light drizzle and rain and snow : : Llovizna ligera y aguacero : Light drizzle and rain shower : : Llovizna ligera y lluvia con tormenta : Light drizzle and rain with thunderstorm : : Llovizna ligera y nieve : Light drizzle and snow : : Llovizna ligera con tormenta : Light drizzle with thunderstorm : : Llovizna helada ligera : Light freezing drizzle : : Llovizna helada ligera y lluvia : Light freezing drizzle and rain : : Llovizna helada ligera y nieve : Light freezing drizzle and snow : : Niebla helada ligera : Light freezing fog : : Lluvia helada ligera y nieve : Light freezing rain and snow : : Granos de nieve helados ligeros y niebla : Light freezing snow grains and fog : : Precipitación desconocida helada ligera : Light freezing unknown precipitation : : Chubasco de granizo ligero : Light hail shower : : Cristales de hielo ligeros : Light ice crystals : : Gránulos de hielo ligeros : Light ice pallets : : Arrastrados ligeros a nivel del suelo : Light low drifting : : Nieve arrastrada ligera a nivel del suelo : Light low drifting snow : : Lluvia ligera y chubasco de granizo : Light rain and hail shower : : Lluvia ligera y granizo con tormenta : Light rain and hail with thunderstorm : : Lluvia ligera y gránulos de hielo : Light rain and ice pallets : : Lluvia ligera y bruma : Light rain and mist : : Lluvia ligera y chubasco de granizo pequeño o nieve granulada : Light rain and small hail/snow pallets shower : : Lluvia ligera y granizo pequeño o nieve granulada con tormenta : Light rain and small hail/snow pallets with thunderstorm : : Lluvia ligera y nieve : Light rain and snow : : Lluvia ligera, nieve y chubasco de granizo pequeño o nieve granulada : Light rain and snow and small hail/snow pallets shower : : Lluvia ligera y granos de nieve : Light rain and snow grains : : Lluvia ligera y chubasco de nieve : Light rain and snow shower : : Lluvia ligera y nieve con tormenta : Light rain and snow with thunderstorm : : Lluvia ligera con tormenta : Light rain with thunderstorm : : Chubasco ligero : Light shower : : Chubascos ligeros de gránulos de hielo : Light showers of ice pellets : : Granizo pequeño o nieve granulada ligera : Light small hail/snow pallets : : Chubasco ligero de granizo pequeño o nieve granulada : Light small hail/snow pallets shower : : Granizo pequeño o nieve granulada ligera con tormenta : Light small hail/snow pallets with thunderstorm : : Nieve ligera y gránulos de hielo : Light snow and ice pallets : : Nieve ligera y granizo pequeño o nieve granulada : Light snow and small hail/snow pallets : : Chubasco de nieve ligera con granizo pequeño o nieve granulada : Light snow and small hail/snow pallets shower : : Nieve ligera con granizo pequeño o nieve granulada y tormenta : Light snow and small hail/snow pallets with thunderstorm : : Nieve ligera y granos de nieve : Light snow and snow grains : : Granos de nieve ligeros : Light snow grains : : Chubasco de nieve ligero : Light snow shower : : Nieve ligera con tormenta : Light snow with thunderstorm : : Tormenta ligera : Light thunderstorm : : Precipitación desconocida ligera : Light unknown precipitation : : Remolinos de polvo o arena ligeros bien desarrollados : Light well-developed dust/sand whirls : : Arena arrastrada a nivel del suelo : Low drifting sand : : Polvo generalizado arrastrado a nivel del suelo : Low drifting widespread dust : : Bruma con tormenta : Mist with thunderstorm : : Lluvia moderada a fuerte con truenos en la zona : Moderate or heavy rain in area with thunder : : Nieve moderada a fuerte con truenos en la zona : Moderate or heavy snow in area with thunder : : Niebla parcial : Partial fog : : Bancos de niebla en las cercanías : Patches of fog in vicinity : : Llovizna helada localizada en las cercanías : Patchy freezing drizzle nearby : : Lluvia ligera localizada con truenos en la zona : Patchy light rain in area with thunder : : Nieve ligera localizada con truenos en la zona : Patchy light snow in area with thunder : : Lluvia localizada en las cercanías : Patchy rain nearby : : Aguanieve localizada en las cercanías : Patchy sleet nearby : : Nieve localizada en las cercanías : Patchy snow nearby : : Lluvia y chubasco de granizo : Rain and hail shower : : Lluvia y granizo con tormenta : Rain and hail with thunderstorm : : Lluvia y bruma : Rain and mist : : Lluvia y bruma con tormenta : Rain and mist with thunderstorm : : Lluvia y chubasco de granizo pequeño o nieve granulada : Rain and small hail/snow pallets shower : : Lluvia y granizo pequeño o nieve granulada con tormenta : Rain and small hail/snow pallets with thunderstorm : : Lluvia y nieve : Rain and snow : : Lluvia, nieve y chubasco de granizo pequeño o nieve granulada : Rain and snow and small hail/snow pallets shower : : Lluvia y granos de nieve : Rain and snow grains : : Lluvia y chubasco de nieve : Rain and snow shower : : Lluvia y nieve con tormenta : Rain and snow with thunderstorm : : Lluvia en las cercanías : Rain in vicinity : : Aguacero en las cercanías : Rain shower in vicinity : : Lluvia con tormenta : Rain with thunderstorm : : Arena : Sand : : Tormenta de arena : Sandstorm : : Niebla poco profunda : Shallow fog : : Bruma poco profunda : Shallow mist : : Chubasco : Shower : : Chubasco en las cercanías : Shower in vicinity : : Granizo pequeño o nieve granulada ligera : Small hail/snow pallets : : Chubasco de granizo pequeño o nieve granulada : Small hail/snow pallets shower : : Granizo pequeño o nieve granulada con tormenta : Small hail/snow pallets with thunderstorm : : Humo : Smoke : : Nieve y gránulos de hielo : Snow and ice pallets : : Nieve y bruma : Snow and mist : : Nieve y chubascos ligeros de granizo o nieve granulada : Snow and small hail/snow pellets shower : : Nieve y chubascos ligeros de granizo o nieve granulada con tormenta : Snow and small hail/snow pallets with thunderstorm : : Nieve y granos de nieve : Snow and snow grains : : Granos de nieve : Snow grains : : Chubasco de nieve : Snow shower : : Nieve con tormenta : Snow with thunderstorm : : Rachas de tormenta : Squalls : : Tormenta : Thunderstorm : : Tormenta en las cercanías : Thunderstorm in vicinity : : Brotes de tormenta en las cercanías : Thundery outbreaks in nearby : : Precipitación desconocida : Unknown precipitation : : Chubasco con precipitación desconocida : Unknown precipitation shower : : Precipitación desconocida con tormenta : Unknown precipitation with thunderstorm : : Ceniza volcánica : Volcanic ash : : Ceniza volcánica en las cercanías : Volcanic ash in vicinity : : Remolinos de polvo o arena bien desarrollados en las cercanías : Well-developed dust/sand whirls in vicinity : : Polvo generalizado : Widespread dust : ================================================ FILE: share/translations/et-help.txt ================================================ Kasutus: $ curl wttr.in # praegune asukoht $ curl wttr.in/tll # ilmaprognoos Tallinna Lennujaamas Toetatud asukohatüübid: /paris # linna nimi /~suur+munamägi # mistahes asukoht /Москва # Ükskõik millises keeles Unicode'i vormingus asukoha nimi /muc # IATA Lennujaama kood (3 tähemärki) /@stackoverflow.com # domeeninimi /94107 # Piirkonna kood (Ainult USA) /-78.46,106.79 # GPS-koordinaadid Erilised asukohad: /moon # Kuu faas (lisage, +US või +France linnade jaoks)) /moon@2016-10-25 # Kuu faas kindlal kuupäeval (@2016-10-25) Ühikud: ?m # meetriline (SI) (kasutatakse vaikimisi kõikjal peale USA) ?u # USCS (kasutusel vaikimisi USA) ?M # kuva tuule kiirust m/s Vaadete valikuid: ?0 # kuva ainult praegune ilm ?1 # kuva praegune ilm + 1 päeva prognoos ?2 # kuva praegune ilm + 2 päeva prognoos ?n # kitsas vaade (Päeva & Öö prognoos) ?q # taltsas variant ('ilmaprognoos' tekstita) ?Q # eriti taltsas variant (asukohata ja 'ilmaprognoos' tekstita) ?T # värvideta PNG valikud: /paris.png # loo PNG fail ?p # lisab raami ümber väljundi ?t # läbipaistvus 150 transparency=... # läbipaistvus 0-st kuni 255-ni (255 = pole läbipaistev) Valikuid saab kombineerida: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNG failis saab valikuid määrata peale _ (alakriips) tähemärki /Rome_0pq_lang=it.png # pikad valikud on eraldatud alakriipsuga Lokaliseerimine: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Toetattud keeled: FULL_TRANSLATION (toetatud) PARTIAL_TRANSLATION (teoksil) Erilehed: /:help # kuvab käesolevat lehte /:bash.function # kuvab soovitatud bash-funktsiooni wttr() /:translation # kuvab infot tõlkijate kohta ================================================ FILE: share/translations/et.txt ================================================ 113: Selge : Clear 113: Päikeseline : Sunny 116: Vahelduv pilvisus : Partly cloudy 119: Pilvine : Cloudy 122: Sombune : Overcast 143: Uduvine : Mist 176: Paiguti vihmasaju võimalus : Patchy rain possible 179: Paiguti lumesaju võimalus : Patchy snow possible 182: Paiguti lörtsisaju võimalus : Patchy sleet possible 185: Paiguti jääudu võimalus : Patchy freezing drizzle possible 200: Äikese võimalus : Thundery outbreaks possible 227: Tuisk : Blowing snow 230: Lumetorm : Blizzard 248: Udu : Fog 260: Jääudu : Freezing fog 263: Paiguti nõrk uduvihm : Patchy light drizzle 266: Nõrk uduvihm : Light drizzle 281: Jääudu : Freezing drizzle 284: Tugev uduvihm : Heavy freezing drizzle 293: Vahelduv nõrk vihm : Patchy light rain 296: Nõrk vihm : Light rain 299: Vahelduv mõõdukas vihm : Moderate rain at times 302: Mõõdukas vihm : Moderate rain 305: Vahelduv tugev vihm : Heavy rain at times 308: Tugev vihm : Heavy rain 311: Nõrk jäävihm : Light freezing rain 314: Mõõdukas või tugev jäävihm : Moderate or heavy freezing rain 317: Nõrk lörtsisadu : Light sleet 320: Mõõdukas või tugev lörtsisadu : Moderate or heavy sleet 323: Paiguti nõrk lumesadu : Patchy light snow 326: Nõrk lumesadu : Light snow 329: Paiguti mõõdukas lumesadu : Patchy moderate snow 332: Mõõdukas lumesadu : Moderate snow 335: Paiguti tugev lumesadu : Patchy heavy snow 338: Tugev lumesadu : Heavy snow 350: Rahe : Ice pellets 353: Nõrk hoogvihm : Light rain shower 356: Mõõdukas või tugev hoogvihm : Moderate or heavy rain shower 359: Padu hoogvihm : Torrential rain shower 362: Nõrk hooglörts : Light sleet showers 365: Mõõdukas või tugev hooglörts : Moderate or heavy sleet showers 368: Nõrk hooglumi : Light snow showers 371: Mõõdukas või tugev hooglumi : Moderate or heavy snow showers 386: Paiguti nõrk vihm äikesega : Patchy light rain with thunder 389: Mõõdukas või tugev vihm äikesega : Moderate or heavy rain with thunder 392: Paiguti nõrk lumesadu äikesega : Patchy light snow with thunder 395: Mõõdukas või tugev lumesadu äikesega : Moderate or heavy snow with thunder : Hoogvihm : Rain shower : Tugev hoogvihm : Heavy showers : Nõrk hoogvihm : Light showers : Tugev hooglumi : Heavy snow showers : Äikesevihm : Thundery showers : Tugev äikesevihm : Thundery heavy rain : Äikesega lumesadu : Thundery snow showers : Tugev hooglumi : Heavy snow showers ================================================ FILE: share/translations/eu-help.txt ================================================ Argibideak: $ curl wttr.in # eguraldia zure kokapenean $ curl wttr.in/bio # eguraldia Bilboko aireportuan Onartzen diren kokapen motak: /bilbao # hiri baten izena /~Bilbao+Guggenheim+museum # leku famatu baten izena /Москва # Edozein lekuko edozein hizkuntzako Unicode izena /bio # aeroportu baten kodea (3 letra) /@stackoverflow.com # web domeinu baten izena /94107 # area kode bat /-78.46,106.79 # GPS koordenadak Leku bereziak: /moon # ilargiaran aldia (gehitu ,+US o ,+France herrialde hauetarako) /moon@2016-10-25 # ilargiaren aldia data konkretu batean (@2016-10-25) Unitateak: ?m # metrikak (SI) (leku guztietako estandarra, AEB ezik) ?u # Lurra sailkatzeko sistema batua edo LSSB (AEB estandarra) ?M # haizeko abiadura erakutsi (m/s) Bistaratzeko aukerak: ?0 # oraingo eguraldia bakarrik ?1 # oraingo eguraldia + egun bateko iragarpena ?2 # oraingo eguraldia + bi egunerako iragarpena ?n # bertsio laburra (eguna eta gaua bakarrik) ?q # bertsio isila ("Eguraldia..." textua barik) ?Q # bertsio super isila ("Eguraldia ..." eta hiriaren izenik ez) ?T # terminalaren sekuentziak desaktibatu (kolorerik ez) PNG aukerak: /paris.png # irudi PNG bat sortzen du ?p # irudiaren inguruan ertz bat gehitzen du ?t # 150 gardentasuna transparency=... # 0tik 255era gardentasuna (255 = gardentasunik ez) Elkarrekin erabili daiteken aukerak: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNG-an aukerak _ karakteren ondoren zehazten dira /Rome_0pq_lang=it.png # aukera sekuentzia luze bat _ karakterearekin bereiz daiteke Kokapena: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Onartzen diren hizkuntzak: FULL_TRANSLATION (onartuak) PARTIAL_TRANSLATION (abian) URL bereziak: /:help # orrialde hau erakutsi /:bash.function # wttr() funtzio bat bash-en iradokitzen du /:translation # itzultzaileei buruzko informazioa erakusten du ================================================ FILE: share/translations/eu.txt ================================================ 114: Oskarbia: Clear 113: Eguzkitsu: Sunny 116: Neurri batean hodeitsu: Partly cloudy 119: Hodeitsu: Cloudy 122: Iluna: Overcast 143: Lanbroa: Mist 176: Posible euri irregularra: Patchy rain possible 179: Posible elurbusti irregularra: Patchy snow possible 182: Posible elurbusti irregularra: Patchy sleet possible 185: Posible zirimiri hotz irregularra: Patchy freezing drizzle possible 200: Posible ekaitz agerraldi: Thundery outbreaks possible 227: Elur-ekaitza: Blowing snow 230: Elur-ekaitza: Blizzard 248: Lainoa: Fog 260: Laino hotza: Freezing fog 263: Garo irregularra: Patchy light drizzle 266: Garoa: Light drizzle 281: Garo hotza: Freezing drizzle 284: Zirimiri hotz-hotza: Heavy freezing drizzle 293: Zirimiri irregularra: Patchy light rain 296: Zirimiri: Light rain 299: Noizbehinkako euri ertaina: Moderate rain at times 302: Euri ertaina: Moderate rain 305: Noizbehinkako euri-zaparrada: Heavy rain at times 308: Euri-zaparrada: Heavy rain 311: Zirimiri hotza: Light freezing rain 314: Euri ertain edo zaparrada hotza: Moderate or heavy freezing rain 317: Elurbusti arina: Light sleet 320: Elurbusti ertain edo gogorra: Moderate or heavy sleet 323: Gazura irregularra: Patchy light snow 326: Gazura: Light snow 329: Noizbehinkako elur ertaina: Patchy moderate snow 332: Elur ertaina: Moderate snow 335: Nieve pesada irregular: Patchy heavy snow 338: Nieve pesada: Heavy snow 350: Txingorra: Ice pellets 353: Zaparrada arina: Light rain shower 356: Zaparrada ertain edo gogorra: Moderate or heavy rain shower 359: Uholde-zaparrada: Torrential rain shower 362: Elurbusti zaparrada arina: Light sleet showers 365: Elurbusti zaparrada ertaina edo gogorra: Moderate or heavy sleet showers 368: Elur zaparrada arina: Light snow showers 371: Elur zaparrada ertaina edo gogorra: Moderate or heavy snow showers 386: Zirimiri irregularra trumoiekin: Patchy light rain with thunder 389: Euri ertaina edo gogorra trumoiekin: Moderate or heavy rain with thunder 392: Elur arina irregularra trumoiekin: Patchy light snow with thunder 395: Elur ertaina edo gogorra trumoiekin: Moderate or heavy snow with thunder ================================================ FILE: share/translations/fa-help.txt ================================================ :نحوه استفاده $ curl wttr.in # موقعیت فعلی $ curl wttr.in/muc # وضعیت اب و هوا در فرودگاه مونیخ :انواع مکان های پشتیبانی شده /paris # نام شهر /~Eiffel+tower # هر مکانی /Москва # نام یونیکد هر مکانی در هر زبانی /muc # کد فرودگاه (3 حرفی) /@stackoverflow.com # نام دامنه /94107 # کد منطقه /-78.46,106.79 # موقعیت جغرافیایی :مکان های خاص /moon # فازهای ماه (+US یا +France را برای این شهرها اضافه کنید) /moon@2016-10-25 # فازهای ماه برای تاریخ خاص (@2016-10-25) :واحدها ?m # متریک (سیستم SI) (به طور پیشفرض در همه جا به جز امریکا استفاده می شود.) ?u # USCS (به طور پیشفرض در امریکا استفاده می شود) ?M # نمایش سرعت باد بر حسب متر بر ثانیه :تنظیمات نمایش ?0 # فقط اب و هوای امروز ?1 # اب و هوای امروز + 1 روز ?2 # اب و هوای امروز + 2 روز ?n # نسخه باریک (فقط روز و شب) ?q # نسخه ساکت (بدون نوشته "گزارش اب و هوا") ?Q # نسخه خیلی ساکت (بدون نوشته "گزارش اب و هوا" و اسم شهر) ?T # حالت بدون رنگ :تنظیمات خروجی PNG /paris.png # ساختن فایل PNG ?p # اضافه کردن حاشیه در خروجی ?t # قرار دادن شفافیت روی 150 transparency=... # شفافیت از 0 تا 255 (255 یعنی بدون شفافیت) :تنظیمات ترکیبی /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # در PNG حالت فایل بعد از _ ذکر می شود /Rome_0pq_lang=it.png # تنظیمات بلند توسط اندرلاین جدا می شوند :محلی سازی $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris :زبان های پشتیبانی شده FULL_TRANSLATION (پشتیبانی شده) PARTIAL_TRANSLATION (در حال پیشرفت) :ادرس های خاص /:help # نمایش این صفحه /:bash.function # نمایش تابع پیشنهادی بش برای wttr() /:translation # نمایش اطلاعات در مورد مترجمین ================================================ FILE: share/translations/fa.txt ================================================ 113: صاف : Clear 113: آفتابی : Sunny 116: نیمه ابری : Partly cloudy 119: ابری : Cloudy 122: پوشیده از ابر : Overcast 143: شبنم : Mist 176: احتمال بارش پراکنده باران : Patchy rain possible 179: احتمال بارش پراکنده برف : Patchy snow possible 182: احتمال بارش پراکنده تگرگ : Patchy sleet possible 185: احتمال بارش پراکنده قطرات منجمد باران : Patchy freezing drizzle possible 200: احتمال رعد و برق : Thundery outbreaks possible 227: بارش سنگین برف : Blowing snow 230: کولاک : Blizzard 248: مه : Fog 260: مه یخ زده : Freezing fog 263: بارش پراکنده نم نم باران : Patchy light drizzle 266: بارش نم نم باران : Light drizzle 281: بارش ریزباران یخ بسته : Freezing drizzle 284: بارش سنگین ریزباران یخ بسته : Heavy freezing drizzle 293: بارش پراکنده باران : Patchy light rain 296: بارش باران : Light rain 299: بارش یکنواخت و ملایم باران : Moderate rain at times 302: بارش ملایم باران : Moderate rain 305: بارش سنگین باران به صورت یکنواخت : Heavy rain at times 308: بارش سنگین باران : Heavy rain 311: بارش نم نم باران یخ بسته : Light freezing rain 314: بارش ملایم یا سنگین باران یخ بسته : Moderate or heavy freezing rain 317: بارش سبک تگرگ : Light sleet 320: بارش ملایم یا سنگین تگرگ : Moderate or heavy sleet 323: بارش پراکنده و سبک برف : Patchy light snow 326: بارش سبک برف : Light snow 329: بارش پراکنده و ملایم برف : Patchy moderate snow 332: بارش ملایم برف : Moderate snow 335: بارش پراکنده و سنگین برف : Patchy heavy snow 338: بارش سنگین برف : Heavy snow 350: بارش دانه های ریز یخ : Ice pellets 353: دوش باران سبک : Light rain shower 356: دوش باران ملایم یا سنگین : Moderate or heavy rain shower 359: دوش باران سیلی : Torrential rain shower 362: بارش سبک دوش تگرگ : Light sleet showers 365: بارش ملایم یا سنگین دوش تگرگ : Moderate or heavy sleet showers 368: بارش سبک دوش برف : Light snow showers 371: بارش ملایم یا سنگین دوش برف : Moderate or heavy snow showers 386: بارش پراکنده باران همراه با رعد و برق : Patchy light rain with thunder 389: بارش ملایم یا سنگین باران همراه با رعد و برق : Moderate or heavy rain with thunder 392: بارش پراکنده برف ملایم یا سنگین همراه با رعد و برق : Patchy light snow with thunder 395: بارش ملایم یا سنگین برف همراه با رعد و برق : Moderate or heavy snow with thunder ================================================ FILE: share/translations/fr-help.txt ================================================ Usage: $ curl wttr.in # emplacement actuel $ curl wttr.in/cdg # météo à l'aéroport de Paris - Charles de Gaulle Types d'emplacements acceptés: /paris # nom de la ville /~Eiffel+tower # emplacement quelconque /Москва # nom Unicode ou emplacement quelconque dans n'importe quelle langue /muc # code aéroport (3 lettres) /@stackoverflow.com # nom de domaine /94107 # code postal (seulement aux États-Unis) /-78.46,106.79 # coordonnées GPS Emplacements particuliers: /moon # phases de la lune (ajouter ,+US ou ,+France pour accéder aux villes du même nom) /moon@2016-10-25 # phases de la lune pour cette date (@2016-10-25) Unités: ?m # système métrique (par défaut partout sauf pour les États-Unis d'Amérique) ?u # USCS (par défaut pour les États-Unis d'Amérique) ?M # affiche la vitesse du vent en m/s Option d'affichage: ?0 # aujourd'hui seulement ?1 # aujourd'hui + demain ?2 # aujourd'hui + 2 jours ?n # version courte (seulement le jour et la nuit) ?q # version silencieuse (pas d'en-tête "Prévisions météo pour") ?Q # version super-silencieuse (pas d'en-tête "Prévisions météo pour", pas de nom de la ville) ?T # séquences d'échappement pour terminaux désactivées (pas de couleurs) Options PNG: /paris.png # génère un fichier PNG ?p # ajoute un cadre autour de la sortie ?t # transparency 150 (transparence 150) transparency=... # transparence de 0 à 255 (255 = pas de transparence) Combiner les options: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # dans le mode PNG les options sont spécifiées après _ /Rome_0pq_lang=it.png # les options longues sont séparées par des underscores _ Localisation: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Langues supportées: FULL_TRANSLATION (Support complet) PARTIAL_TRANSLATION (Support incomplet) URLs particulières: /:help # affiche cette page /:bash.function # fonction bash recommandée wttr() /:translation # affiche les informations sur la traduction de wttr.in ================================================ FILE: share/translations/fr.txt ================================================ 113: Ensoleillé : Sunny : 114: Temps clair : Clear : 116: Partiellement couvert : Partly cloudy : 119: Nuageux : Cloudy : 122: Couvert : Overcast : 143: Brumeux : Mist : 176: Pluies éparses possibles : Patchy rain possible : 179: Chutes de neige éparses possibles : Patchy snow possible : 182: Chutes éparses de neige fondue possibles : Patchy sleet possible : 185: Bruines givrantes éparses possibles : Patchy freezing drizzle possible : 200: Orages possibles : Thundery outbreaks possible : 227: Poudrerie : Blowing snow : 230: Blizzard : Blizzard : 248: Brouillard : Fog : 260: Brouillard givrant : Freezing fog : 263: Bruines éparses et légères : Patchy light drizzle : 266: Bruine légère : Light drizzle : 281: Bruine givrante : Freezing drizzle : 284: Forte bruine givrante : Heavy freezing drizzle : 293: Pluies éparses et légères : Patchy light rain : 296: Pluie légère : Light rain : 299: Pluie modérée intermittente : Moderate rain at times : 302: Pluie modérée : Moderate rain : 305: Forte pluie intermittente : Heavy rain at times : 308: Forte pluie : Heavy rain : 311: Pluie verglaçante légère : Light freezing rain : 314: Pluie verglaçante modérée à forte : Moderate or heavy freezing rain : 317: Chutes légères de neige fondue : Light sleet : 320: Chutes de neige fondue modérées à fortes : Moderate or heavy sleet : 323: Chutes de neige éparses et légères : Patchy light snow : 326: Chutes de neige légères : Light snow : 329: Chutes de neige éparses et modérées : Patchy moderate snow : 332: Chutes de neige modérées : Moderate snow : 335: Fortes chutes de neige éparses : Patchy heavy snow : 338: Fortes chutes de neige : Heavy snow : 350: Grésil : Ice pellets : 353: Averses légères : Light rain shower : 356: Averses modérées à fortes : Moderate or heavy rain shower : 359: Averses torrentielles : Torrential rain shower : 362: Averses légères de neige fondue : Light sleet showers : 365: Averses de neige fondue modérées à fortes : Moderate or heavy sleet showers : 368: Légères averses de neige : Light snow showers : 371: Averses de neige modérées à fortes : Moderate or heavy snow showers : 386: Pluies légères et éparses avec tonnerre : Patchy light rain with thunder : 389: Pluies orageuses modérées à fortes : Moderate or heavy rain with thunder : 392: Neige légère et éparse avec tonnerre : Patchy light snow with thunder : 395: Chutes de neige orageuses modérées à fortes : Moderate or heavy snow with thunder : : Brume à proximité : Haze in vicinity : : Fortes pluies et averses de neige : Heavy rain and snow shower : : Fortes averses de pluie : Heavy rain shower : : Bruine légère et grains de neige : Light drizzle and snow grains : : Averse légère de pluie et de brume : Light rain and mist shower : : Averse légère de grêlons/flocons de neige : Light small hail/snow pellets shower : : Averse légère de précipitations inconnues : Light unknown precipitation shower : : Pluie et grêlons/flocons de neige avec orage : Rain and small hail/snow pellets with thunderstorm : : Averse de neige à proximité : Snow shower in vicinity : : Éclats orageux à proximité : Thundery outbreaks nearby : : Poudrerie basse : Low drifting snow : : Plaques de brouillard : Patches of fog : : Bruine : Drizzle : : Bruine légère et pluie : Light drizzle and rain : : Neige : Snow : : Pluie : Rain : : Averse de pluie : Rain Shower : : Sable soufflé : Blowing sand : : Souffle de sable étendu : Blowing widespread dust : : Souffle de sable étendu à proximité : Blowing widespread dust in vicinity : : Bruine et brouillard : Drizzle and fog : : Bruine et pluie : Drizzle and rain : : Bruine et averse de pluie : Drizzle and rain shower : : Bruine avec orage : Drizzle with thunderstorm : : Tempête de poussière : Dust storm : : Brouillard à proximité : Fog in vicinity : : Bruine et pluie verglaçante : Freezing drizzle and rain : : Brume verglaçante : Freezing mist : : Pluie verglaçante : Freezing rain : : Pluie verglaçante et neige : Freezing rain and snow : : Précipitations inconnues verglaçantes : Freezing unknown precipitation : : Nuage en entonnoir à proximité : Funnel cloud in vicinity : : Averse de grêle : Hail shower : : Grêle avec orage : Hail with thunderstorm : : Brume : Haze : : Forte poudrerie : Heavy blowing snow : : Forte bruine : Heavy drizzle : : Forte bruine et pluie : Heavy drizzle and rain : : Fortes précipitations inconnues verglaçantes : Heavy freezing unknown precipitation : : Forte grêle avec orage : Heavy hail with thunderstorm : : Fort grésil : Heavy ice pallets : : Fortes pluies et averses de grêle : Heavy rain and hail shower : : Fortes pluies et grêle avec orage : Heavy rain and hail with thunderstorm : : Fortes pluies et averses de grêlons/flocons de neige : Heavy rain and small hail/snow pallets shower : : Fortes pluies et averses de grêlons/flocons de neige avec orage : Heavy rain and small hail/snow pallets with thunderstorm : : Forte pluie et neige : Heavy rain and snow : : Forte pluie et neige, averses de grêlons/flocons de neige avec orage : Heavy rain and snow and small hail/snow pallets with thunderstorm : : Pluie forte avec orage : Heavy rain with thunderstorm : : Forte averse de grêlons/flocons de neige : Heavy small hail/snow pallets shower : : Forte averse de grêlons/flocons de neige avec orage : Heavy small hail/snow pallets with thunderstorm : : Fortes chutes de neige et averses de grêlons/flocons de neige : Heavy snow and small hail/snow pallets shower : : Fortes chutes de neige et averses de grêlons/flocons de neige avec orage : Heavy snow and small hail/snow pallets with thunderstorm : : Fortes chutes de neige : Heavy snow shower : : Fortes chutes de neige avec orage : Heavy snow with thunderstorm : : Cristaux de glace : Ice crystals : : Grésil : Ice pallets : : Légère poudrerie : Light blowing snow : : Bruine légère et brouillard : Light drizzle and fog : : Bruine légère et grésil : Light drizzle and ice pallets : : Bruine légère, pluie et neige : Light drizzle and rain and snow : : Bruine légère et averse de pluie : Light drizzle and rain shower : : Bruine légère et pluie avec orage : Light drizzle and rain with thunderstorm : : Bruine légère et neige : Light drizzle and snow : : Bruine légère avec orage : Light drizzle with thunderstorm : : Bruine verglaçante légère : Light freezing drizzle : : Légère bruine verglaçante et pluie : Light freezing drizzle and rain : : Légère bruine verglaçante et neige : Light freezing drizzle and snow : : Léger brouillard givrant : Light freezing fog : : Légère pluie verglaçante et neige : Light freezing rain and snow : : Légers grains de neige verglaçants et brouillard : Light freezing snow grains and fog : : Légères précipitations verglaçantes inconnues : Light freezing unknown precipitation : : Légère averse de grêle : Light hail shower : : Légers cristaux de glace : Light ice crystals : : Léger grésil : Light ice pallets : : Souffle léger près du sol : Light low drifting : : Poudrerie légère et basse : Light low drifting snow : : Pluie légère et averse de grêle : Light rain and hail shower : : Pluie légère et grêle avec orage : Light rain and hail with thunderstorm : : Pluie légère et grésil : Light rain and ice pallets : : Pluie légère et brume : Light rain and mist : : Pluie légère et averses de grêlons/flocons de neige : Light rain and small hail/snow pallets shower : : Pluie légère et averses de grêlons/flocons de neige avec orage : Light rain and small hail/snow pallets with thunderstorm : : Pluie légère et neige : Light rain and snow : : Pluie légère et neige et averses de grêlons/flocons de neige : Light rain and snow and small hail/snow pallets shower : : Légère pluie et grains de neige : Light rain and snow grains : : Légère pluie et averses de neige : Light rain and snow shower : : Légère pluie et neige avec orage : Light rain and snow with thunderstorm : : Légère pluie avec orage : Light rain with thunderstorm : : Averse légère : Light shower : : Averse légère de grésil : Light showers of ice pellets : : Légers grêlons/flocons de neige : Light small hail/snow pallets : : Averse légère de grêlons/flocons de neige : Light small hail/snow pallets shower : : Légers grêlons/flocons de neige avec orage : Light small hail/snow pallets with thunderstorm : : Neige légère et grésil : Light snow and ice pallets : : Neige légère et chutes de grêlons/flocons de neige : Light snow and small hail/snow pallets : : Neige légère et averses de grêlons/flocons de neige : Light snow and small hail/snow pallets shower : : Neige légère et averses de grêlons/flocons de neige avec orage : Light snow and small hail/snow pallets with thunderstorm : : Neige légère et grains de neige : Light snow and snow grains : : Légers grains de neige : Light snow grains : : Légère averse de neige : Light snow shower : : Neige légère avec orage : Light snow with thunderstorm : : Orage léger : Light thunderstorm : : Légères précipitations inconnues : Light unknown precipitation : : Tourbillons légers et bien developpés de poussière/sable : Light well-developed dust/sand whirls : : Léger souffle de sable bas : Low drifting sand : : Léger souffle de poussière étendue : Low drifting widespread dust : : Brume avec orage : Mist with thunderstorm : : Pluie modérée ou forte avec orage : Moderate or heavy rain in area with thunder : : Neige modérée ou forte avec orage : Moderate or heavy snow in area with thunder : : Brouillard partiel : Partial fog : : Plaques de brouillard à proximité : Patches of fog in vicinity : : Bruine verglaçante éparse à proximité : Patchy freezing drizzle nearby : : Pluie légère et éparse localisée avec orage : Patchy light rain in area with thunder : : Neige légère et éparse localisée avec orage : Patchy light snow in area with thunder : : Pluie éparse à proximité : Patchy rain nearby : : Pluie verglaçante éparse à proximité : Patchy sleet nearby : : Neige éparse à proximité : Patchy snow nearby : : Averse de pluie et grêle : Rain and hail shower : : Pluie et grêle avec orage : Rain and hail with thunderstorm : : Pluie et brume : Rain and mist : : Pluie et brume avec orage : Rain and mist with thunderstorm : : Pluie et averses de grêlons/flocons de neige : Rain and small hail/snow pallets shower : : Pluie et averses de grêlons/flocons de neige avec orage : Rain and small hail/snow pallets with thunderstorm : : Pluie et neige : Rain and snow : : Pluie et neige avec averses de grêlons/flocons de neige : Rain and snow and small hail/snow pallets shower : : Pluie et grains de neige : Rain and snow grains : : Averse de pluie et neige : Rain and snow shower : : Pluie et neige avec orage : Rain and snow with thunderstorm : : Pluie à proximité : Rain in vicinity : : Averse de pluie à proximité : Rain shower in vicinity : : Pluie avec orage : Rain with thunderstorm : : Sable : Sand : : Tempête de sable : Sandstorm : : Brouillard léger : Shallow fog : : Brume légère : Shallow mist : : Averse : Shower : : Averse à proximité : Shower in vicinity : : Grêlons/flocons de neige : Small hail/snow pallets : : Averse de grêlons/flocons de neige : Small hail/snow pallets shower : : Averse de grêlons/flocons de neige avec orage : Small hail/snow pallets with thunderstorm : : Fumée : Smoke : : Neige et grésil : Snow and ice pallets : : Neige et brume : Snow and mist : : Neige et averses de grêlons/flocons de neige : Snow and small hail/snow pellets shower : : Neige et averses de grêlons/flocons de neige avec orage : Snow and small hail/snow pallets with thunderstorm : : Neige et grains de neige : Snow and snow grains : : Grains de neige : Snow grains : : Averse de neige : Snow shower : : Neige avec orage : Snow with thunderstorm : : Bourrasques : Squalls : : Orage : Thunderstorm : : Orage à proximité : Thunderstorm in vicinity : : Éclats orageux à proximité : Thundery outbreaks in nearby : : Précipitations inconnues : Unknown precipitation : : Averse de précipitations inconnues : Unknown precipitation shower : : Précipitations inconnues avec orage : Unknown precipitation with thunderstorm : : Cendres volcaniques : Volcanic ash : : Cendres volcaniques à proximité : Volcanic ash in vicinity : : Tourbillons de sable/poussière bien développés à proximité : Well-developed dust/sand whirls in vicinity : : Poussière étendue : Widespread dust : ================================================ FILE: share/translations/fy.txt ================================================ 113: Helder : Clear 113: Sinnich : Sunny 116: Bytsje bewolkt : Partly cloudy 119: Bewolkt : Cloudy 122: Bewolkt : Overcast 143: Mistich : Mist 176: Misskien lokaal reinbui : Patchy rain possible 179: Misskien lokaal snie : Patchy snow possible 182: Misskien lokaal hagel : Patchy sleet possible 185: Misskien lokaal izel : Patchy freezing drizzle possible 200: Misskien in onwaarsbui : Thundery outbreaks possible 227: Snie wyn : Blowing snow 230: Sniestoarm : Blizzard 248: Mistich : Fog 260: Rijp : Freezing fog 263: Lokaal motrein : Patchy light drizzle 266: Motrein : Light drizzle 281: Izel : Freezing drizzle 284: Swiere izel : Heavy freezing drizzle 293: Lokaal in bytsje rein : Patchy light rain 296: Bytsje rein : Light rain 299: Út en troch rein : Moderate rain at times 302: Rein : Moderate rain 305: Út en troch swiere rein : Heavy rain at times 308: Swiere rein : Heavy rain 311: Lichte izel : Light freezing rain 314: Swiere izel : Moderate or heavy freezing rain 317: Lichte hagel : Light sleet 320: Swiere hagel : Moderate or heavy sleet 323: Lokaal lichte snie : Patchy light snow 326: Lichte snie : Light snow 329: Lokaal snie : Patchy moderate snow 332: Snie : Moderate snow 335: Lokaal swiere snie : Patchy heavy snow 338: Swiere snie : Heavy snow 350: Hagel : Ice pellets 353: Lichte reinbuien : Light rain shower 356: Reinbuien : Moderate or heavy rain shower 359: Ekstreem swiere reinbuien : Torrential rain shower 362: Lichte hagelbuien : Light sleet showers 365: Hagelbuien : Moderate or heavy sleet showers 368: Lichte sniebuien : Light snow showers 371: Sniebuien : Moderate or heavy snow showers 386: Lokaal lichte rein mei onwaar: Patchy light rain with thunder weer 389: Rein mei onwaar : Moderate or heavy rain with thunder 392: Lokaal lichte snie mei onwaar: Patchy light snow with thunder nweer 395: Snie mei onwaar : Moderate or heavy snow with thunder ================================================ FILE: share/translations/ga.txt ================================================ 113: Geal : Clear 113: Grianmhar : Sunny 116: Breacscamallach : Partly cloudy 119: Scamallach : Cloudy 122: Scamallach : Overcast 143: Ceochán : Mist 176: Ceathanna : Patchy rain possible 179: Ceathanna sneachta : Patchy snow possible 182: Ceathanna flichshneachta : Patchy sleet possible 185: Ceathanna cloichshneachta : Patchy freezing drizzle possible 200: Ceathanna toirní : Thundery outbreaks possible 227: Stealladh sneachta : Blowing snow 230: Síobadh sneachta : Blizzard 248: Ceo : Fog 260: Ceo reoite : Freezing fog 263: Ceobhrán : Patchy light drizzle 266: Ceobhrán éadrom : Light drizzle 281: Ceobhrán reoite : Freezing drizzle 284: Ceobhrán reoite trom : Heavy freezing drizzle 293: Breachbháisteach éadrom : Patchy light rain 296: Báisteach éadrom : Light rain 299: Báisteach mheasartha uaireanta : Moderate rain at times 302: Báisteach mheasartha : Moderate rain 305: Báisteach throm uairanta : Heavy rain at times 308: Báisteach throm : Heavy rain 311: Báisteach reoite éadrom : Light freezing rain 314: Báisteach mheasartha nó reoite throm : Moderate or heavy freezing rain 317: Flichshneachta éadrom : Light sleet 320: Flichshneachta measartha nó trom : Moderate or heavy sleet 323: Breacsneachta éadrom : Patchy light snow 326: Sneachta éadrom : Light snow 329: Breacsneachta measartha : Patchy moderate snow 332: Sneachta measartha : Moderate snow 335: Sneachta trom uaireanta : Patchy heavy snow 338: Sneachta trom : Heavy snow 350: Chloichshneachta : Ice pellets 353: Cith éadrom : Light rain shower 356: Cith measartha no trom : Moderate or heavy rain shower 359: Duartan : Torrential rain shower 362: Ceathanna flichshneachta : Light sleet showers 365: Ceathanna flichshneachta measartha nó troma : Moderate or heavy sleet showers 368: Ceathanna sneachta éadroma : Light snow showers 371: Ceathanna sneachta measartha nó troma : Moderate or heavy snow showers 386: Ceathanna éadroma le toirneach : Patchy light rain with thunder 389: Báisteach measartha nó throm le tóirneach : Moderate or heavy rain with thunder 392: Sneachta éadrom le toirneach : Patchy light snow with thunder 395: Sneachta measartha nó trom le toirneach : Moderate or heavy snow with thunder ================================================ FILE: share/translations/gl-help.txt ================================================ Instrucións: $ curl wttr.in # o tempo na sua localización actual $ curl wttr.in/muc # o tempo no aeroporto de Múnic Tipos de localización soportados: /paris # o nome dunha cidade /~Eiffel+tower # o nome de calquera lugar famoso /Москва # nome Unicode de calquera lugar en calquera idioma /muc # o código dun aeroporto (3 letras) /@stackoverflow.com # o nome dun dominio web /94107 # um código de área /-78.46,106.79 # coordenadas do GPS Lugares especiais: /moon # A fase da lúa (crecente ,+US ou ,+France para estas cidades) /moon@2016-10-25 # A fase da lúa nunha determinada data (@2016-10-25) Unidades: ?m # Métricas (SI) (por defecto en todos os lugares agás en EEUU) ?u # Sistema Unificado de Clasificación de Solo ou USCS (por defecto en EEUU) ?M # Amosar a velocidade do vento en m/s Opcións de visualización: ?0 # Soamente o clima actual ?1 # O clima actual + a previsión de 1 dia ?2 # O clima actual + a previsión de 2 dias ?n # Versión curta (só o dia e a noite) ?q # Versión breve (sen o texto de "Previsión do Tempo") ?Q # Versión superbreve (sen "Previsión do Tempo" e o nome da cidade) ?T # Desactiva as secuencias de escape no terminal (sen cores) Opións de PNG: /paris.png # Xera unha imaxe PNG ?p # Amece un borde ao redor da imaxe ?t # Transparencia 150 transparency=... # Transparencia de 0 a 255 (255 = sen transparencia) As opcións poden ser usadas en conxunto: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # Em PNG as opcións especificanse depois do caracter _ /Rome_0pq_lang=it.png # Nunha secuencia longa de opcións, poden ser separadas polo caracter _ Localizaión: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Linguas soportadas: FULL_TRANSLATION (soportadas) PARTIAL_TRANSLATION (en proceso) URLs especiais: /:help # Amosa esta páxina /:bash.function # Suxire unha función wttr() en bash /:translation # Amosa información respecto dos tradutores ================================================ FILE: share/translations/gl.txt ================================================ 113: Despexado : Clear 113: Solleiro : Sunny 116: Parcialmente Nubrado : Partly cloudy 119: Nubrado : Cloudy 122: Cuberto : Overcast 143: Neboa : Mist 176: Posibel choiva : Patchy rain possible 179: Posibel neve : Patchy snow possible 182: Posibel auganeve : Patchy sleet possible 185: Posibel barruzo xeado : Patchy freezing drizzle possible 200: Posibeis treboadas : Thundery outbreaks possible 227: Cebrisca : Blowing snow 230: Treboada de neve : Blizzard 248: Brétema : Fog 260: Brétema xeada : Freezing fog 263: Barruzo lixeiro casual : Patchy light drizzle 266: Barruzo lixeiro : Light drizzle 281: Barruzo xeado : Freezing drizzle 284: Barruzo xeado forte : Heavy freezing drizzle 293: Chuvisca casual : Patchy light rain 296: Chuvisca : Light rain 299: Choiva casual : Moderate rain at times 302: Choiva : Moderate rain 305: Dioivo casual : Heavy rain at times 308: Dioivo : Heavy rain 311: Orballo xeado : Light freezing rain 314: Xistra : Moderate or heavy freezing rain 317: Auganeve lixeira : Light sleet 320: Auganeve : Moderate or heavy sleet 323: Nevarisca casual : Patchy light snow 326: Nevarisca : Light snow 329: Nevada casual : Patchy moderate snow 332: Nevada : Moderate snow 335: Nevada forte casual : Patchy heavy snow 338: Nevada forte : Heavy snow 350: Sarabia : Ice pellets 353: Orballo : Light rain shower 356: Bategada : Moderate or heavy rain shower 359: Choiva torrencial : Torrential rain shower 362: Torba : Light sleet showers 365: Torba forte : Moderate or heavy sleet showers 368: Choiva con neve lixeira : Light snow showers 371: Choiva con neve forte : Moderate or heavy snow showers 386: Barruzo casual con tronos : Patchy light rain with thunder 389: Treboada : Moderate or heavy rain with thunder 392: Nevarisca casual con tronos : Patchy light snow with thunder 395: Nevada con tronos : Moderate or heavy snow with thunder ================================================ FILE: share/translations/gu-help.txt ================================================ Usage: $ curl wttr.in # current location $ curl wttr.in/muc # weather in the Munich airport Supported location types: /paris # city name /~Eiffel+tower # any location (+ for spaces) /Москва # Unicode name of any location in any language /muc # airport code (3 letters) /@stackoverflow.com # domain name /94107 # area codes /-78.46,106.79 # GPS coordinates Moon phase information: /moon # Moon phase (add ,+US or ,+France for these cities) /moon@2016-10-25 # Moon phase for the date (@2016-10-25) Units: m # metric (SI) (used by default everywhere except US) u # USCS (used by default in US) M # show wind speed in m/s View options: 0 # only current weather 1 # current weather + today's forecast 2 # current weather + today's + tomorrow's forecast A # ignore User-Agent and force ANSI output format (terminal) F # do not show the "Follow" line n # narrow version (only day and night) q # quiet version (no "Weather report" text) Q # superquiet version (no "Weather report", no city name) T # switch terminal sequences off (no colors) PNG options: /paris.png # generate a PNG file p # add frame around the output t # transparency 150 transparency=... # transparency from 0 to 255 (255 = not transparent) background=... # background color in form RRGGBB, e.g. 00aaaa Options can be combined: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # in PNG the file mode are specified after _ /Rome_0pq_lang=it.png # long options are separated with underscore Localization: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Supported languages: FULL_TRANSLATION (supported) PARTIAL_TRANSLATION (in progress) Special URLs: /:help # show this page /:bash.function # show recommended bash function wttr() /:translation # show the information about the translators ================================================ FILE: share/translations/gu.txt ================================================ : Fortes pluies et orages de grêle : Heavy rain and hail with thunderstorm : Fortes pluies orageuses : Heavy rain with thunderstorm : Orages de pluie et grêle légères : Light rain and hail with thunderstorm : Pluie légère et averses : Light rain and snow shower : Pluies légères orageuses : Light rain with thunderstorm : Chutes de neige légères : Light snow shower : Nappes de brouillard : Partial fog : Orage de pluie et de grêle : Rain and hail with thunderstorm : Pluies orageuses : Rain with thunderstorm : Brouillard léger : Shallow fog : Brume : Smoke : Grains : Squalls : Orages proches : Thunderstorm in vicinity : Neige : Snow : Pluie : Rain : Pluie légère, Averses : Light Rain, Rain Shower : Averses : Rain Shower : Nappes de brouillard : Patches of fog : Bruine : Drizzle : Bruine légère : Light drizzle : Chasse-neige basse : Low drifting snow : Pluie et neige légères : Light rain and snow : Averses proches : Shower in vicinity : Pluie et orages : Rain with thunderstorm : Averse de pluie et neige mêlées : Rain and snow shower : Orage : Thunderstorm : Bruine et pluie : Drizzle and rain : Orage de grêle : Hail with thunderstorm : Brume : Haze : Bruine legère et pluie : Light drizzle and rain : Orage, pluie légère et grèle / neige roulée : Light rain and small hail/snow pallets with thunderstorm 113 : Temps clair : Clear 113 : Ensoleillé : Sunny 116 : Partiellement couvert : Partly cloudy 119 : Nuageux : Cloudy 122 : Couvert : Overcast 143 : Brumeux : Mist 176 : Pluies éparses possibles : Patchy rain possible 179 : Chutes de neige éparses possibles : Patchy snow possible 182 : Chutes éparses de neige fondue possibles : Patchy sleet possible 185 : Bruines givrantes éparses possibles : Patchy freezing drizzle possible 200 : Orages possibles : Thundery outbreaks possible 227 : Poudrerie : Blowing snow 230 : Blizzard : Blizzard 248 : Brouillard : Fog 260 : Brouillard givrant : Freezing fog 263 : Bruines éparses et légères : Patchy light drizzle 266 : Bruine légère : Light drizzle 281 : Bruine givrante : Freezing drizzle 284 : Forte bruine givrante : Heavy freezing drizzle 293 : Pluies éparses et légères : Patchy light rain 296 : Pluie légère : Light rain 299 : Pluie modérée intermittente : Moderate rain at times 302 : Pluie modérée : Moderate rain 305 : Forte pluie intermittente : Heavy rain at times 308 : Forte pluie : Heavy rain 311 : Pluie verglaçante légère : Light freezing rain 314 : Pluie verglaçante modérée à forte : Moderate or heavy freezing rain 317 : Chutes légères de neige fondue : Light sleet 320 : Chutes de neige fondue modérées à fortes : Moderate or heavy sleet 323 : Chutes de neige éparses et légères : Patchy light snow 326 : Chutes de neige légères : Light snow 329 : Chutes de neige éparses et modérées : Patchy moderate snow 332 : Chutes de neige modérées : Moderate snow 335 : Fortes chutes de neige éparses : Patchy heavy snow 338 : Fortes chutes de neige : Heavy snow 350 : Grésil : Ice pellets 353 : Averses légères : Light rain shower 356 : Averses modérées à fortes : Moderate or heavy rain shower 359 : Averses torrentielles : Torrential rain shower 362 : Averses légères de neige fondue : Light sleet showers 365 : Averses de neige fondue modérées à fortes : Moderate or heavy sleet showers 368 : Averses de neige légères : Light snow showers 371 : Averses de neige modérées à fortes : Moderate or heavy snow showers 386 : Pluies orageuses légères et éparses : Patchy light rain with thunder 389 : Pluies orageuses modérées à fortes : Moderate or heavy rain with thunder 392 : Chutes de neige orageuses légères et éparses : Patchy light snow with thunder 395 : Chutes de neige orageuses modérées à fortes : Moderate or heavy snow with thunder ================================================ FILE: share/translations/he.txt ================================================ 113 : בהיר: Clear 113 : שמשי: Sunny 116 : מעונן חלקית : Partly cloudy 119 : מעונן : Cloudy 122 : מעונן : Overcast 143 : ערפל : Mist 176 : סיכוי לגשם מקומי : Patchy rain possible 179 : סיכוי לשלג מקומי : Patchy snow possible 182 : סיכוי לגשם מעורב בשלג מקומי : Patchy sleet possible 185 : סיכוי לברד מקומי : Patchy freezing drizzle possible 200 : סיכוי לסופת ברקים : Thundery outbreaks possible 227 : משבי שלג : Blowing snow 230 : סופת שלג : Blizzard 248 : ערפל : Fog 260 : קרה : Freezing fog 263 : ברד מקומי קל : Patchy light drizzle 266 : ברד קל : Light drizzle 281 : ברד : Freezing drizzle 284 : ברד כבד : Heavy freezing drizzle 293 : גשם מקומי קל : Patchy light rain 296 : גשם קל : Light rain 299 : גשם לסירוגין : Moderate rain at times 302 : גשם : Moderate rain 305 : גשם כבד לסירוגין : Heavy rain at times 308 : גשם כבד : Heavy rain 311 : גשם קופא קל : Light freezing rain 314 : גשם קופא בינוני עד כבד : Moderate or heavy freezing rain 317 : גשם מעורב בשלג קל : Light sleet 320 : גשם מעורב בשלג בינוני עד כבד : Moderate or heavy sleet 323 : שלג מקומי קל : Patchy light snow 326 : שלג קל : Light snow 329 : שלג מקומי בינוני : Patchy moderate snow 332 : שלג בינוני : Moderate snow 335 : שלג מקומי כבד : Patchy heavy snow 338 : שלג כבד : Heavy snow 350 : ברד : Ice pellets 353 : גשם קל : Light rain shower 356 : גשם בינוני עד כבד : Moderate or heavy rain shower 359 : ממטרים כבדים : Torrential rain shower 362 : גשם מעורב בשלג קל : Light sleet showers 365 : גשם מעורב בשלג בינוני עד כבד : Moderate or heavy sleet showers 368 : שלג קל : Light snow showers 371 : שלג בינוני עד כבד : Moderate or heavy snow showers 386 : גשם מקומי קל ורעמים : Patchy light rain with thunder 389 : גשם בינוני עד כבד ורעמים : Moderate or heavy rain with thunder 392 : שלג קל ורעמים : Patchy light snow with thunder 395 : שלג בינוני עד כבד ורעמים : Moderate or heavy snow with thunder ================================================ FILE: share/translations/hi-help.txt ================================================ उपयोग:     $ curl wttr.in          # वर्तमान स्थान के मौसम की जानकारी      $ curl wttr.in/muc      # म्यूनिख हवाई अड्डे का मौसम समर्थित स्थान प्रकार:     /paris                  # शहर का नाम     /~Eiffel+tower          # कोई भी स्थान (एक से अधिक शब्दो को जोड़ने के लिए + का उपयोग करे)     /Москва                 # किसी भी भाषा में किसी भी स्थान का यूनिकोड नाम     /muc                    # एयरपोर्ट कोड (3 अक्षर)     /@stackoverflow.com     # डोमेन नाम     /94107                  # क्षेत्र कोड     /-78.46,106.79          # जीपीएस निर्देशांक चंद्र चरण की जानकारी:     /moon                   # चंद्रमा चरण (शहरों के लिए +US या, +France जोड़ें)     /moon@2016-10-25        # तिथि के लिए चंद्र चरण (@2016-10-25) इकाइयां:     m                       # मेट्रिक (एसआई) (यूएस को छोड़कर हर जगह डिफ़ॉल्ट रूप से उपयोग किया जाता है)     u                       # यूएससीएस (यूएस में डिफ़ॉल्ट रूप से प्रयुक्त)     M                       # हवा की गति मीटर/सेकंड में दिखाएं विकल्प देखें:     0                       # केवल वर्तमान मौसम     1                       # वर्तमान मौसम और आज का पूर्वानुमान     2                       # वर्तमान मौसम और आज का और कल का पूर्वानुमान     A                       # युसेर-एजेंट को अनदेखा करें और एएनएसआई आउटपुट (टर्मिनल) को बाध्य करें     F                       # "फॉलो" लाइन न दिखाएं     n                       # संकीर्ण संस्करण (केवल दिन और रात के लिये)     q                       # शांत संस्करण (कोई "मौसम रिपोर्ट" पाठ नहीं)     Q                       # अति शांत संस्करण ("मौसम की जानकारी" और शहर का नाम नहीं)     T                       # टर्मिनल अनुक्रम बंद करें (रंगो के बिना) पीएनजी विकल्प:     /paris.png              # पीएनजी फ़ाइल जनरेट करें     p                       # आउटपुट के चारों ओर फ्रेम जोड़ें     t                       # पारदर्शिता 150     transparency=...        # पारदर्शिता 0 से 255 तक (255 = पारदर्शी नहीं)     background=...          # RRGGBB के रूप में पृष्ठभूमि का रंग, जैसे की 00aaaa विकल्पों को जोड़ा जा सकता है:     /Paris?0pq     /Paris?0pq&lang=fr     /Paris_0pq.png          # पीएनजी में फ़ाइल मोड को "_" के बाद निर्दिष्ट किया जाता है     /Rome_0pq_lang=it.png   # लंबे विकल्पों को अंडरस्कोर से अलग किया जाता है स्थानीयकरण:     $ curl fr.wttr.in/Paris     $ curl wttr.in/paris?lang=fr     $ curl -H "Accept-Language: fr" wttr.in/paris समर्थित भाषाएँ:     FULL_TRANSLATION (समर्थित)     PARTIAL_TRANSLATION (प्रगति में) विशेष यूआरएल:     /:help                  # इस पेज को दिखाएं     /:bash.function         # अनुशंसित बैश फ़ंक्शन wttr () दिखाएं     /:translation           # अनुवादकों के बारे में जानकारी दिखाएं ================================================ FILE: share/translations/hi.txt ================================================ : तेज बारिश और गरज के साथ ओलावृष्टि : Heavy rain and hail with thunderstorm : गरज के साथ तेज बारिश : Heavy rain with thunderstorm : गरज के साथ हल्की बारिश और ओलावृष्टि : Light rain and hail with thunderstorm : हल्की बारिश और बर्फ की बौछार : Light rain and snow shower : गरज के साथ हल्की बारिश : Light rain with thunderstorm : हल्की बर्फ़ की बौछार : Light snow shower : आंशिक कोहरा : Partial fog : घना कोहरा : Patchy fog : गरज के साथ बारिश और ओलावृष्टि : Rain and hail with thunderstorm : गरज के साथ बारिश : Rain with thunderstorm : उथला कोहरा : Shallow fog : धुआं : Smoke : तूफ़ान : Squalls : आसपास में आंधी : Thunderstorm in vicinity : हिमपात : Snow : वर्षा : Rain : हल्की बारिश, बारिश की बौछार : Light Rain, Rain Shower : बारिश की बौछार : Rain Shower : कोहरे के धब्बे : Patches of fog : बूंदा बांदी : Drizzle : हल्की बूंदाबांदी : Light drizzle : कम बहती बर्फ : Low drifting snow : हल्की बारिश और हिमपात : Light rain and snow : आस-पास शावर : Shower in vicinity : गरज के साथ बारिश : Rain with thunderstorm : बारिश और बर्फ की बौछार : Rain and snow shower : आंधी तूफान : Thunderstorm : बूंदा बांदी और बारिश : Drizzle and rain : गरज के साथ ओलावृष्टि : Hail with thunderstorm : धुंध : Haze : हल्की बूंदा बांदी और बारिश : Light drizzle and rain : गरज के साथ हल्की बारिश और छोटे-छोटे ओले/बर्फ की पट्टियां : Light rain and small hail/snow pallets with thunderstorm 113 : स्पष्ट : Clear 113 : धूपदार : Sunny 116 : आंशिक रूप से बादल छाएंगे : Partly cloudy 119 : बादल : Cloudy 122 : घटाटोप : Overcast 143 : कोहरा : Mist 176 : हल्की बारिश संभव : Patchy rain possible 179 : हल्की बर्फ़बारी संभव : Patchy snow possible 182 : हल्की ओले के साथ वर्षा संभव : Patchy sleet possible 185 : जमने वाली हल्की बूंदाबांदी संभव : Patchy freezing drizzle possible 200 : गरज का प्रकोप संभव : Thundery outbreaks possible 227 : उड़ाने वाली बर्फ : Blowing snow 230 : बर्फानी तूफान : Blizzard 248 : कोहरा : Fog 260 : अत्यधिक ठंडा कोहरा : Freezing fog 263 : हल्की हल्की बूंदा बांदी : Patchy light drizzle 266 : हल्की बूंदाबांदी : Light drizzle 281 : जमा देने वाली हवा : Freezing drizzle 284 : भारी जमने वाली बूंदा बांदी : Heavy freezing drizzle 293 : हल्की हल्की बारिश : Patchy light rain 296 : हलकी बारिश : Light rain 299 : कभी-कभी मध्यम बारिश : Moderate rain at times 302 : औसत दर्जे की वर्षा : Moderate rain 305 : रुक-रुक कर हो रही भारी बारिश : Heavy rain at times 308 : भारी वर्षा : Heavy rain 311 : हल्की जमने वाली बारिश : Light freezing rain 314 : मध्यम या भारी जमने वाली बारिश : Moderate or heavy freezing rain 317 : हल्की ओले के साथ वर्षा : Light sleet 320 : मध्यम या भारी ओले के साथ वर्षा : Moderate or heavy sleet 323 : हल्की हल्की बर्फ : Patchy light snow 326 : हल्की बर्फ : Light snow 329 : हल्की मध्यम हिमपात : Patchy moderate snow 332 : मध्यम हिमपात : Moderate snow 335 : हल्की भारी हिमपात : Patchy heavy snow 338 : भारी हिमपात : Heavy snow 350 : ओले के साथ वर्षा : Ice pellets 353 : हल्की बोछारे : Light rain shower 356 : मध्यम या भारी बारिश की बौछार : Moderate or heavy rain shower 359 : मूसलाधार बारिश की बौछार : Torrential rain shower 362 : हल्की ओले के साथ बौछारें : Light sleet showers 365 : मध्यम से भारी ओले के साथ बौछारें : Moderate or heavy sleet showers 368 : हल्की बर्फ़बारी : Light snow showers 371 : मध्यम या भारी हिमपात की बौछार : Moderate or heavy snow showers 386 : गरज के साथ हल्की बारिश : Patchy light rain with thunder 389 : गरज के साथ मध्यम या भारी बारिश : Moderate or heavy rain with thunder 392 : गरज के साथ हल्की हल्की बर्फ़ : Patchy light snow with thunder 395 : गरज के साथ मध्यम या भारी हिमपात : Moderate or heavy snow with thunder ================================================ FILE: share/translations/hr.txt ================================================ : Kiša : Rain : 113 : Vedro : Clear : 113 : Sunčano : Sunny : 116 : Djelomično oblačno : Partly cloudy : 119 : Oblačno : Cloudy : 122 : Tmurno : Overcast : 143 : Magla : Mist : 176 : Moguća povremena kiša : Patchy rain possible : 179 : Moguć povremeni snijeg : Patchy snow possible : 182 : Moguća povremena susnježica : Patchy sleet possible : 185 : Moguća ledena rosulja : Patchy freezing drizzle possible : 200 : Moguć prolom oblaka : Thundery outbreaks possible : 227 : Nanosi snijega : Blowing snow : 230 : Mećava : Blizzard : 248 : Magla : Fog : 260 : Ledena magla : Freezing fog : 263 : Povremena rosulja : Patchy light drizzle : 266 : Lagana rosulja : Light drizzle : 281 : Ledena rosulja : Freezing drizzle : 284 : Jaka ledena rosulja : Heavy freezing drizzle : 293 : Povremena lagana kiša : Patchy light rain : 296 : Lagana kiša : Light rain : 299 : Umjerena povremena kiša : Moderate rain at times : 302 : Umjerena kiša : Moderate rain : 305 : Povremeni pljuskovi : Heavy rain at times : 308 : Jaka kiša : Heavy rain : 311 : Lagana ledena kiša : Light freezing rain : 314 : Umjerena ili jaka ledena kiša : Moderate or heavy freezing rain : 317 : Lagana susnježica : Light sleet : 320 : Umjerena ili jaka susnježica : Moderate or heavy sleet : 323 : Povremen lagan snijeg : Patchy light snow : 326 : Lagan snijeg : Light snow : 329 : Povremen umjeren snijeg : Patchy moderate snow : 332 : Umjeren snijeg : Moderate snow : 335 : Povremen jak snijeg : Patchy heavy snow : 338 : Jak snijeg : Heavy snow : 350 : Ledena tuča : Ice pellets : 353 : Lagani pljusak : Light rain shower : 356 : Umjeren ili jak pljusak : Moderate or heavy rain shower : 359 : Obilan pljusak : Torrential rain shower : 362 : Lagana susnježica : Light sleet showers : 365 : Umjerena ili jaka susnježica : Moderate or heavy sleet showers : 368 : Lagani snježni pljuskovi : Light snow showers : 371 : Umjereni ili jaki snježni pljuskovi : Moderate or heavy snow showers : 386 : Povremena lagana kiša s grmljavinom : Patchy light rain with thunder : 389 : Umjerena ili jaka kiša s grmljavinom : Moderate or heavy rain with thunder : 392 : Povremen lagan snijeg s grmljavinom : Patchy light snow with thunder : 395 : Umjeren ili jak snijeg s grmljavinom : Moderate or heavy snow with thunder : : Maglica u blizini : Haze in vicinity : : Jak kišni i snježni pljusak : Heavy rain and snow shower : : Jak kišni pljusak : Heavy rain shower : : Lagana kišica i snježni kristali : Light drizzle and snow grains : : Lagana kiša i pljusak magle : Light rain and mist shower : : Lagan pljusak sitne tuče/snježnih kuglica : Light small hail/snow pellets shower : : Lagan pljusak nepoznatih oborina : Light unknown precipitation shower : : Kiša i sitna tuča/snježne kuglice s grmljavinom : Rain and small hail/snow pellets with thunderstorm : : Snježni pljusak u blizini : Snow shower in vicinity : : Grmljavinske oluje u blizini : Thundery outbreaks nearby : : Nisko vijanje snijega : Low drifting snow : : Polja magle : Patches of fog : : Kišica : Drizzle : : Lagana kišica i kiša : Light drizzle and rain : : Snijeg : Snow : : Kišni pljusak : Rain Shower : : Vijanje pijeska : Blowing sand : : Široko rasprostranjeno vijanje prašine : Blowing widespread dust : : Široko rasprostranjeno vijanje prašine u blizini : Blowing widespread dust in vicinity : : Kišica i magla : Drizzle and fog : : Kišica i kiša : Drizzle and rain : : Kišica i kišni pljusak : Drizzle and rain shower : : Kišica s grmljavinom : Drizzle with thunderstorm : : Pješčana oluja : Dust storm : : Magla u blizini : Fog in vicinity : : Smrzavajuća kišica i kiša : Freezing drizzle and rain : : Smrzavajuća magla : Freezing mist : : Smrzavajuća kiša : Freezing rain : : Smrzavajuća kiša i snijeg : Freezing rain and snow : : Smrzavajuće nepoznate oborine : Freezing unknown precipitation : : Lijevasta oblak u blizini : Funnel cloud in vicinity : : Pljusak tuče : Hail shower : : Tuča s grmljavinom : Hail with thunderstorm : : Maglica : Haze : : Jako vijanje snijega : Heavy blowing snow : : Jaka kišica : Heavy drizzle : : Jaka kišica i kiša : Heavy drizzle and rain : : Jake smrzavajuće nepoznate oborine : Heavy freezing unknown precipitation : : Jaka tuča s grmljavinom : Heavy hail with thunderstorm : : Jaki ledeni kristali : Heavy ice pallets : : Jaka kiša i pljusak tuče : Heavy rain and hail shower : : Jaka kiša i tuča s grmljavinom : Heavy rain and hail with thunderstorm : : Jaka kiša i sitna tuča/snježne kuglice pljusak : Heavy rain and small hail/snow pallets shower : : Jaka kiša i sitna tuča/snježne kuglice s grmljavinom : Heavy rain and small hail/snow pallets with thunderstorm : : Jaka kiša i snijeg : Heavy rain and snow : : Jaka kiša i snijeg te sitna tuča/snježne kuglice s grmljavinom : Heavy rain and snow and small hail/snow pallets with thunderstorm : : Jaka kiša s grmljavinom : Heavy rain with thunderstorm : : Jak pljusak sitne tuče/snježnih kuglica : Heavy small hail/snow pallets shower : : Jaka sitna tuča/snježne kuglice s grmljavinom : Heavy small hail/snow pallets with thunderstorm : : Jak snijeg i pljusak sitne tuče/snježnih kuglica : Heavy snow and small hail/snow pallets shower : : Jak snijeg i sitna tuča/snježne kuglice s grmljavinom : Heavy snow and small hail/snow pallets with thunderstorm : : Jak snježni pljusak : Heavy snow shower : : Jak snijeg s grmljavinom : Heavy snow with thunderstorm : : Ledeni kristali : Ice crystals : : Ledeni kristali : Ice pallets : : Lagan vijanje snijega : Light blowing snow : : Lagana kišica i magla : Light drizzle and fog : : Lagana kišica i ledeni kristali : Light drizzle and ice pallets : : Lagana kišica i kiša i snijeg : Light drizzle and rain and snow : : Lagana kišica i kišni pljusak : Light drizzle and rain shower : : Lagana kišica i kiša s grmljavinom : Light drizzle and rain with thunderstorm : : Lagana kišica i snijeg : Light drizzle and snow : : Lagana kišica s grmljavinom : Light drizzle with thunderstorm : : Lagan smrzavajući kišica : Light freezing drizzle : : Lagan smrzavajući kišica i kiša : Light freezing drizzle and rain : : Lagan smrzavajući kišica i snijeg : Light freezing drizzle and snow : : Lagan smrzavajući magla : Light freezing fog : : Lagan smrzavajući kiša i snijeg : Light freezing rain and snow : : Lagan smrzavajući snježni kristali i magla : Light freezing snow grains and fog : : Lagan smrzavajući nepoznate oborine : Light freezing unknown precipitation : : Lagan pljusak tuče : Light hail shower : : Lagani ledeni kristali : Light ice crystals : : Lagani ledeni kristali : Light ice pallets : : Lagan nisko vijanje : Light low drifting : : Lagan nisko vijanje snijega : Light low drifting snow : : Lagana kiša i pljusak tuče : Light rain and hail shower : : Lagana kiša i tuča s grmljavinom : Light rain and hail with thunderstorm : : Lagana kiša i ledeni kristali : Light rain and ice pallets : : Lagana kiša i magla : Light rain and mist : : Lagana kiša i pljusak sitne tuče/snježnih kuglica : Light rain and small hail/snow pallets shower : : Lagana kiša i sitna tuča/snježne kuglice s grmljavinom : Light rain and small hail/snow pallets with thunderstorm : : Lagana kiša i snijeg : Light rain and snow : : Lagana kiša i snijeg te pljusak sitne tuče/snježnih kuglica : Light rain and snow and small hail/snow pallets shower : : Lagana kiša i snježni kristali : Light rain and snow grains : : Lagana kiša i snježni pljusak : Light rain and snow shower : : Lagana kiša i snijeg s grmljavinom : Light rain and snow with thunderstorm : : Lagana kiša s grmljavinom : Light rain with thunderstorm : : Lagan pljusak : Light shower : : Lagan pljusak ledenih kuglica : Light showers of ice pellets : : Lagan sitna tuča/snježne kuglice : Light small hail/snow pallets : : Lagan pljusak sitne tuče/snježnih kuglica : Light small hail/snow pallets shower : : Lagan sitna tuča/snježne kuglice s grmljavinom : Light small hail/snow pallets with thunderstorm : : Lagan snijeg i ledeni kristali : Light snow and ice pallets : : Lagan snijeg i sitna tuča/snježne kuglice : Light snow and small hail/snow pallets : : Lagan snježni pljusak sa sitnom tučom/snježnim kuglicama : Light snow and small hail/snow pallets shower : : Lagan snijeg sa sitnom tučom/snježnim kuglicama i grmljavinom : Light snow and small hail/snow pallets with thunderstorm : : Lagan snijeg i snježni kristali : Light snow and snow grains : : Lagan snježni kristali : Light snow grains : : Lagan snježni pljusak : Light snow shower : : Lagan snijeg s grmljavinom : Light snow with thunderstorm : : Lagan grmljavina : Light thunderstorm : : Lagan nepoznate oborine : Light unknown precipitation : : Lagano razvijeni vrtlozi prašine ili pijeska : Light well-developed dust/sand whirls : : Nisko vijanje pijeska : Low drifting sand : : Nisko vijanje široko rasprostranjene prašine : Low drifting widespread dust : : Magla s grmljavinom : Mist with thunderstorm : : Umjerena do jaka kiša s grmljavinom : Moderate or heavy rain in area with thunder : : Umjeren do jak snijeg s grmljavinom : Moderate or heavy snow in area with thunder : : Djelomična magla : Partial fog : : Polja magle u blizini : Patches of fog in vicinity : : Povremeno smrzavajuća kišica u blizini : Patchy freezing drizzle nearby : : Povremeno lagana kiša s grmljavinom u području : Patchy light rain in area with thunder : : Povremeno lagan snijeg s grmljavinom u području : Patchy light snow in area with thunder : : Povremeno kiša u blizini : Patchy rain nearby : : Povremeno susnježica u blizini : Patchy sleet nearby : : Povremeno snijeg u blizini : Patchy snow nearby : : Kiša i pljusak tuče : Rain and hail shower : : Kiša i tuča s grmljavinom : Rain and hail with thunderstorm : : Kiša i magla : Rain and mist : : Kiša i magla s grmljavinom : Rain and mist with thunderstorm : : Kiša i pljusak sitne tuče/snježnih kuglica : Rain and small hail/snow pallets shower : : Kiša i sitna tuča/snježne kuglice s grmljavinom : Rain and small hail/snow pallets with thunderstorm : : Kiša i snijeg : Rain and snow : : Kiša i snijeg te pljusak sitne tuče/snježnih kuglica : Rain and snow and small hail/snow pallets shower : : Kiša i snježni kristali : Rain and snow grains : : Kiša i snježni pljusak : Rain and snow shower : : Kiša i snijeg s grmljavinom : Rain and snow with thunderstorm : : Kiša u blizini : Rain in vicinity : : Kišni pljusak u blizini : Rain shower in vicinity : : Kiša s grmljavinom : Rain with thunderstorm : : Pijesak : Sand : : Pješčana oluja : Sandstorm : : Plitka magla : Shallow fog : : Plitka maglica : Shallow mist : : Pljusak : Shower : : Pljusak u blizini : Shower in vicinity : : Sitna tuča/snježne kuglice : Small hail/snow pallets : : Pljusak sitne tuče/snježnih kuglica : Small hail/snow pallets shower : : Sitna tuča/snježne kuglice s grmljavinom : Small hail/snow pallets with thunderstorm : : Dim : Smoke : : Snijeg i ledeni kristali : Snow and ice pallets : : Snijeg i magla : Snow and mist : : Snijeg i lagan pljusak sitne tuče/snježnih kuglica : Snow and small hail/snow pellets shower : : Snijeg i lagan pljusak sitne tuče/snježnih kuglica s grmljavinom : Snow and small hail/snow pallets with thunderstorm : : Snijeg i snježni kristali : Snow and snow grains : : Snježni kristali : Snow grains : : Snježni pljusak : Snow shower : : Snijeg s grmljavinom : Snow with thunderstorm : : Grmljavinske oluje : Squalls : : Grmljavina : Thunderstorm : : Grmljavina u blizini : Thunderstorm in vicinity : : Grmljavinske oluje u blizini : Thundery outbreaks in nearby : : Nepoznate oborine : Unknown precipitation : : Pljusak s nepoznatim oborinama : Unknown precipitation shower : : Nepoznate oborine s grmljavinom : Unknown precipitation with thunderstorm : : Vulkanski pepeo : Volcanic ash : : Vulkanski pepeo u blizini : Volcanic ash in vicinity : : Dobro razvijeni vrtlozi prašine/pijeska u blizini : Well-developed dust/sand whirls in vicinity : : Široko rasprostranjena prašina : Widespread dust : ================================================ FILE: share/translations/hu-help.txt ================================================ Használat: $ curl wttr.in # jelenlegi tartózkodási hely $ curl wttr.in/muc # időjárás a müncheni repülőtéren Támogatott pozíciómeghatározások: /paris # város /~Eiffel+tower # bármilyen hely /Москва # Unicode név bármilyen nyelven /muc # repülőtér kód (3 betű) /@stackoverflow.com # domain név /94107 # körzetszám /-78.46,106.79 # GPS koordináták Speciális helyek: /moon # holdfázis (a végére tehető ,+US vagy ,+France például) /moon@2016-10-25 # holdfázis az adott dátumhoz (@2016-10-25) Mértékegységek: m # metrikus (SI) (használatban mindenhol az USA kivételével) u # USA hagyományos (alapértelmezett az USA-ban) M # szélsebesség m/s-ban Megjelenítési beállítások: 0 # csak az aktuális időjárás 1 # aktuális időjárás + 1 nap 2 # aktuális időjárás + 2 nap A # User-Agent figyelmen kívül hagyása, ANSI output kikényszerítése (terminálokban) F # ne jeneítse meg a "Kövesd" sort n # keskeny változat (csak nappal és éjszaka) q # csendes verzió ("Időjárás előrejelzés" szöveg nélkül) Q # szupercsendes verzió ("Időjárás előrejelzés" szöveg és hely nélkül) T # terminál szekvenciák kikapcsolása (színek nélküli megjelenés) PNG beállítások: /paris.png # PNG kép készítése p # keret a kép köré t # 150-es "átlátszóság" transparency=... # "átlátszóság" 0 és 255 között (255 = egyáltalán nem átlátszó) A beállítások kombinálhatók: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNG módban az opciók egy aláhúzásjel után megadhatók /Rome_0pq_lang=it.png # a hosszú opcióneveket aláhúzásjelekkel kell elválasztani Megjelenítés különböző nyelveken: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Támogatott nyelvek: FULL_TRANSLATION (teljesen támogatott) PARTIAL_TRANSLATION (folyamatban...) Speciális URL-ek: /:help # segítség (ez az oldal) /:bash.function # ajánlott bash függvény /:translation # információ a fordítókról ================================================ FILE: share/translations/hu.txt ================================================ 113: Derült : Clear 113: Napos : Sunny 116: Közepesen felhős : Partly cloudy 119: Felhős : Cloudy 122: Borult : Overcast 143: Pára : Mist 176: Szórványos zápor : Patchy rain possible 179: Szórványos havazás : Patchy snow possible 182: Szórványos havas eső : Patchy sleet possible 185: Szórványos ónos szitálás : Patchy freezing drizzle possible 200: Viharos széllökések előfordulhatnak : Thundery outbreaks possible 227: Hófúvás : Blowing snow 230: Hóvihar : Blizzard 248: Köd : Fog 260: Zúzmarás köd : Freezing fog 263: Szórványos gyenge szitálás : Patchy light drizzle 266: Gyenge szitálás : Light drizzle 281: Ónos szitálás : Freezing drizzle 284: Erős ónos szitálás : Heavy freezing drizzle 293: Szórványos gyenge eső : Patchy light rain 296: Gyenge eső : Light rain 299: Időnként eső : Moderate rain at times 302: Eső : Moderate rain 305: Időnként intenzív eső : Heavy rain at times 308: Intenzív eső : Heavy rain 311: Gyenge ónos eső : Light freezing rain 314: Ónos eső : Moderate or heavy freezing rain 317: Gyenge havas eső : Light sleet 320: Havas eső : Moderate or heavy sleet 323: Szórványos hószállingózás : Patchy light snow 326: Hószállingózás : Light snow 329: Szórványos havazás : Patchy moderate snow 332: Havazás : Moderate snow 335: Szórványos intenzív havazás : Patchy heavy snow 338: Intenzív havazás : Heavy snow 350: Jégeső : Ice pellets 353: Gyenge zápor : Light rain shower 356: Zápor : Moderate or heavy rain shower 359: Zuhogó záporeső : Torrential rain shower 362: Gyenge havas záporeső : Light sleet showers 365: Havas záporeső : Moderate or heavy sleet showers 368: Gyenge hózápor : Light snow showers 371: Hózápor : Moderate or heavy snow showers 386: Szórványos zivatar : Patchy light rain with thunder 389: Zivatar : Moderate or heavy rain with thunder 392: Szórványos hózivatar : Patchy light snow with thunder 395: Hózivatar : Moderate or heavy snow with thunder : Hóvúvás : Low drifting snow : Ködfoltok : Patches of fog : Szitálás : Drizzle : Gyenge szitálás : Light drizzle : Havazás : Snow : Eső : Rain : Gyenge eső : Light Rain : Talajmenti köd : Shallow fog ================================================ FILE: share/translations/hy.txt ================================================ 113: Պարզ : Clear : Ясно 113: Արևոտ : Sunny : Солнечно 116: Մասամբ ամպամած : Partly cloudy : Переменная облачность 119: Ամպամած : Cloudy : Облачно 122: Ամպամած : Overcast : Пасмурно 143: Խոնավ : Mist : Дымка 176: Տեղ-տեղ անձրև : Patchy rain possible : Местами дождь 179: Տեղ-տեղ ձյուն : Patchy snow possible : Местами снег 182: Տեղ-տեղ ձյունախառն անձրև : Patchy sleet possible : Местами дождь со снегом 185: Տեղ-տեղ սառեցնող մրրիկ : Patchy freezing drizzle possible : Местами замерзающая морось 200: Ամպրոպ : Thundery outbreaks possible : Местами грозы 227: Ձնաբուք : Blowing snow : Поземок 230: Ձնաբուք : Blizzard : Метель 248: Մառախուղ : Fog : Туман 260: Սառեցնող մառախուղ : Freezing fog : Переохлажденный туман 263: Տեղ-տեղ թույլ մրրիկ : Patchy light drizzle : Местами слабая морось 266: Թույլ մրրիկ : Light drizzle : Слабая морось 281: Սառեցնող մրրիկ : Freezing drizzle : Замерзающая морось 284: ՈՒժեղ սառեցնող մրրիկ : Heavy freezing drizzle : Сильная замерзающая морось 293: Տեղ-տեղ թույլ անձրև : Patchy light rain : Местами небольшой дождь 296: Թույլ անձրև : Light rain : Небольшой дождь 299: Չափավոր անձրև երբեմն : Moderate rain at times : Временами умеренный дождь 302: Չափավոր անձրև : Moderate rain : Умеренный дождь 305: Ուժեղ անձրև երբեմն : Heavy rain at times : Временами сильный дождь 308: Ուժեղ անձրև : Heavy rain : Сильный дождь 311: Թույլ սառեցնող անձրև : Light freezing rain : Слабый переохлажденный дождь 314: Չափավոր կամ ուժեղ սառեցնող անձրև : Moderate or heavy freezing rain : Умеренный или сильный переохлажденный дождь 317: Թույլ մանրակարկուտ : Light sleet : Небольшой дождь со снегом 320: Չափավոր կամ ուժեղ մանրակարկուտ : Moderate or heavy sleet : Умеренный или сильный дождь со снегом 323: Տեղ-տեղ թույլ ձյուն : Patchy light snow : Местами небольшой снег 326: Թույլ ձյուն : Light snow : Небольшой снег 329: Տեղ-տեղ չափավոր ձյուն : Patchy moderate snow : Местами умеренный снег 332: Չափավոր ձյուն : Moderate snow : Умеренный снег 335: Տեղ-տեղ ուժեղ ձյուն : Patchy heavy snow : Местами сильный снег 338: ՈՒժեղ ձյուն : Heavy snow : Сильный снег 350: Սառցաբեկորներ : Ice pellets : Ледяной дождь 353: Թույլ տեղատարափ անձրև : Light rain shower : Небольшой ливневый дождь 356: Չափավոր կամ ուժեղ տեղատարափ անձրև : Moderate or heavy rain shower : Умеренный или сильный ливневый дождь 359: Ուժեղ տեղատարափ անձրև : Torrential rain shower : Очень сильный ливень 362: Թույլ ձյունախառն անձրև : Light sleet showers : Небольшой ливневый дождь со снегом 365: Չափավոր կամ ուժեղ ձյունախառն անձրև ։ Moderate or heavy sleet showers : Небольшой снег 368: Թույլ ձյունախառն անձրև : Light snow showers : Небольшой ливневый дождь со снегом 371: Չափավոր կամ ուժեղ ձյան տարափ : Moderate or heavy snow showers : Умеренный или сильный снежный ливень 386: Տեղ-տեղ թույլ անձրև և ամպրոպ : Patchy light rain with thunder : Местами небольшой дождь с грозой 389: Չափավոր կամ ուժեղ անձրև և ամպրոպ : Moderate or heavy rain with thunder : Умеренный или сильный дождь с грозой 392: Տեղ-տեղ թույլ ձյուն և ամպրոպ : Patchy light snow with thunder : Местами небольшой снег с грозой 395: Ուժեղ ձյուն և ամպրոպ : Moderate or heavy snow with thunder : Умеренный или сильный снег с грозой ================================================ FILE: share/translations/ia-help.txt ================================================ Instructiones: $ curl wttr.in # le tempore a vostre location actual $ curl wttr.in/muc # le tempore del aeroporto de Múnich Typos de location appoiava: /paris # le nomine de un citate /~Eiffel+tower # le nomine de qualque sito famose /Москва # le nomine Unicode de qualque sito in qualque lingua /muc # le codice de un aeroporto (3 litteras) /@stackoverflow.com # le nomine de un dominio internet /94107 # un codice de area /-78.46,106.79 # coordinatas de GPS Sitos speciales: /moon # le phase del luna (adjunge ,+US o ,+France por iste paises) /moon@2016-10-25 # le phase del luna in un data specific (@2016-10-25) Unitates: ?m # metric (SI) (le standardo in tote le sitos extra in EEUU) ?u # Systema Uniforme de Classification del Solo o USCS (le standardo in EEUU) ?M # demonstrar le velocitate del vento in m/s Optiones visual: ?0 # solmente le tempore actual ?1 # le tempore actual + le prevision de 1 die ?2 # le tempore actual + le prevision de 2 die ?n # version curte (solmente le die et le nocte) ?q # version silentiose (sin le texto de "Le tempore a") ?Q # version supersilentiose (nec "Le tempore a" nec le nomine del citate) ?T # desactiva le sequentias de terminal (sin colores) Optiones de PNG: /paris.png # genera un imagine PNG ?p # adde un frontiera circa le imagine ?t # transparentia 150 transparency=... # transparentia de 0 a 255 (255 = sin transparentia) Le optiones pote usar insimul: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # in PNG le optiones sera post que le character _ /Rome_0pq_lang=it.png # un grande secuentia de optiones pote seperate per le character _ Location: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Linguas appoiava: FULL_TRANSLATION (appoiava) PARTIAL_TRANSLATION (in progresso) URLs speciales: /:help # monstra isto pagina /:bash.function # proponera un function wttr() in bash /:translation # monstra aviso super le traductores ================================================ FILE: share/translations/ia.txt ================================================ 113: Clar : Clear 113: Allegre : Sunny 116: Pauc Nubilose : Partly cloudy 119: Nubilose : Cloudy 122: Gris : Overcast 143: Nebula : Mist 176: Pauc pluvia possibile : Patchy rain possible 179: Pauc nive possibile : Patchy snow possible 182: Pauc nive miscite con pluvia : Patchy sleet possible 185: Pauc nive glacial con pluvia : Patchy freezing drizzle possible 200: Rar tonitro possibile : Thundery outbreaks possible 227: Ventose nive : Blowing snow 230: Tempesta de nive : Blizzard 248: Bruma : Fog 260: Bruma glacial : Freezing fog 263: Pauc pluvia : Patchy light drizzle 266: Pluveva : Light drizzle 281: Pluveva glacial : Freezing drizzle 284: Multe pluvia glacial : Heavy freezing drizzle 293: Pauc pluvia : Patchy light rain 296: Pluveva : Light rain 299: Pluvia moderate a vices : Moderate rain at times 302: Pluvia moderate : Moderate rain 305: Multe pluvia a vices : Heavy rain at times 308: Multe pluvia : Heavy rain 311: Pauc pluvia glacial : Light freezing rain 314: Multe o moderate pluvia glacial : Moderate or heavy freezing rain 317: Pauc nive miscite con pluvia : Light sleet 320: Multe o moderate nive miscite : Moderate or heavy sleet 323: Breve nive : Patchy light snow 326: Pauc nive : Light snow 329: Breve nive moderate : Patchy moderate snow 332: Nive moderate : Moderate snow 335: Multe nive a veces : Patchy heavy snow 338: Multe nive : Heavy snow 350: Bolletas de glacie : Ice pellets 353: Pauc pluvia : Light rain shower 356: Multe o moderate pluvia : Moderate or heavy rain shower 359: Tempesta de pluvia : Torrential rain shower 362: Pauc pluvia con glacie : Light sleet showers 365: Multe o moderate pluvia con glacie : Moderate or heavy sleet showers 368: Pauc nive con pluvia : Light snow showers 371: Multe o moderate nive con pluvia : Moderate or heavy snow showers 386: Pauc pluvia con tonitro : Patchy light rain with thunder 389: Moderate pluvia con tonitro : Moderate or heavy rain with thunder 392: Pauc nive con tonitro : Patchy light snow with thunder 395: Moderate o multe nive con tonitro : Moderate or heavy snow with thunder ================================================ FILE: share/translations/id-help.txt ================================================ Cara penggunaan: $ curl wttr.in # lokasi saat ini $ curl wttr.in/muc # cuaca di bandara Munich Dukungan tipe lokasi: /paris # nama kota /~Eiffel+tower # sembarang lokasi /Москва # nama Unicode dari semabarang lokasi dalam sembarang bahasa /muc # kode bandara (3 huruf) /@stackoverflow.com # nama domain /94107 # kode area /-78.46,106.79 # koordinat GPS Lokasi khusus: /moon # bentuk Bulan (tambahkan ,+US atau ,+France untuk kota-kota tersebut) /moon@2016-10-25 # bentuk Bulan untuk tanggal (@2016-10-25) Satuan: ?m # metrik (SI) (digunakan sebagai standar di semua tempat kecuali US) ?u # USCS (digunakan sebagai standar di US) ?M # tampilkan kecepatan angin dalam m/s Opsi tampilan: ?0 # hanya cuaca saat ini ?1 # cuaca saat ini + 1 hari ?2 # cuaca saat ini + 2 hari ?n # versi sempit (hanya siang dan malam) ?q # versi diam (tanpa teks "Laporan cuaca") ?Q # versi sangat diam (tanpa teks "Laporan cuaca", tanpa nama kota) ?T # ganti urutan terminal (tanpa warna) Opsi PNG: /paris.png # hasilkan suatu berkas PNG ?p # tambahkan bingkai di sekeliling hasil ?t # transparansi 150 transparency=... # transparansi dari 0 sampai 255 (255 = tidak transparan) Opsi dapat digabung: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # dalam PNG mode berkas ditentukan sesudah _ /Rome_0pq_lang=it.png # opsi panjang dipisahkan dengan underscore Lokalisasi: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Dukungan bahasa: FULL_TRANSLATION (supported) PARTIAL_TRANSLATION (in progress) URL khusus: /:help # tampilkan halaman ini /:bash.function # tampilkan fungsi bash yang direkomendasikan wttr() /:translation # tampilkan informasi tentang translator ================================================ FILE: share/translations/id.txt ================================================ 113: Langit bersih : Clear 113: Cerah : Sunny 116: Sebagian berawan : Partly cloudy 119: Berawan : Cloudy 122: Mendung : Overcast 143: Berkabut : Mist 176: Mungkin hujan tidak merata : Patchy rain possible 179: Mungkin bersalju tidak merata : Patchy snow possible 182: Mungkin hujan es tidak merata : Patchy sleet possible 185: Mungkin gerimis beku tidak merata : Patchy freezing drizzle possible 200: Mungkin banyak petir : Thundery outbreaks possible 227: Hembusan salju : Blowing snow 230: Badai salju : Blizzard 248: Kabut : Fog 260: Kabut beku : Freezing fog 263: Gerimis ringan tidak merata : Patchy light drizzle 266: Gerimis ringan : Light drizzle 281: Gerimis beku : Freezing drizzle 284: Gerimis beku rapat : Heavy freezing drizzle 293: Hujan ringan tidak merata : Patchy light rain 296: Hujan ringan : Light rain 299: Hujan di beberapa waktu : Moderate rain at times 302: Hujan : Moderate rain 305: Hujan lebat di beberapa waktu : Heavy rain at times 308: Hujan lebat : Heavy rain 311: Hujan beku ringan : Light freezing rain 314: Hujan beku : Moderate or heavy freezing rain 317: Hujan es ringan : Light sleet 320: Hujan es : Moderate or heavy sleet 323: Salju ringan tidak merata : Patchy light snow 326: Salju ringan : Light snow 329: Salju tidak merata : Patchy moderate snow 332: Salju : Moderate snow 335: Salju lebat tidak merata : Patchy heavy snow 338: Salju lebat : Heavy snow 350: Bongkahan es : Ice pellets 353: Hujan ringan : Light rain shower 356: Hujan lebat : Moderate or heavy rain shower 359: Hujan sangat lebat : Torrential rain shower 362: Hujan es ringan : Light sleet showers 365: Hujan es : Moderate or heavy sleet showers 368: Hujan salju ringan : Light snow showers 371: Hujan salju : Moderate or heavy snow showers 386: Hujan ringan dengan petir tidak merata : Patchy light rain with thunder 389: Hujan dengan petir : Moderate or heavy rain with thunder 392: Salju ringan dengan petir tidak merata : Patchy light snow with thunder 395: Salju dengan petir : Moderate or heavy snow with thunder ================================================ FILE: share/translations/is.txt ================================================ 113 : Heiðskýrt : Clear 113 : Sól : Sunny 116 : Léttskýjað : Partly cloudy 119 : Skýjað : Cloudy 122 : Alskýjað : Overcast 143 : Mistur : Mist 176 : Hætta á rigningu á köflum : Patchy rain possible 179 : Hætta á snjókomu á köflum : Patchy snow possible 182 : Hætta á slyddu á köflum : Patchy sleet possible 185 : Hætta á frostúða á köflum : Patchy freezing drizzle possible 200 : Hætta á þrumuveðri : Thundery outbreaks possible 227 : Skafrenningur : Blowing snow 230 : Snjóbylur : Blizzard 248 : Þoka : Fog 260 : Frostþoka : Freezing fog 263 : Lítilsháttar súld á köflum : Patchy light drizzle 266 : Lítilsháttar súld : Light drizzle 281 : Frostúði : Freezing drizzle 284 : Mikill frostúði : Heavy freezing drizzle 293 : Lítilsháttar rigning á köflum : Patchy light rain 296 : Lítilsháttar rigning : Light rain 299 : Dálítil rigning á köflum : Moderate rain at times 302 : Dálítil rigning : Moderate rain 305 : Mikil rigning á köflum : Heavy rain at times 308 : Mikil rigning : Heavy rain 311 : Lítilsháttar frostrigning : Light freezing rain 314 : Dálítið eða mikið frostregn : Moderate or heavy freezing rain 317 : Lítilsháttar slydda : Light sleet 320 : Dálítil eða mikil slydda : Moderate or heavy sleet 323 : Lítilsháttar snjókoma á köflum : Patchy light snow 326 : Lítilsháttar snjókoma : Light snow 329 : Dálítil snjókoma á köflum : Patchy moderate snow 332 : Dálítil snjókoma : Moderate snow 335 : Mikill snjókoma á köflum : Patchy heavy snow 338 : Mikill snjókoma : Heavy snow 350 : Haglél : Ice pellets 353 : Skúrir : Light rain shower 356 : Dálitlir eða mikilir Skúrir : Moderate or heavy rain shower 359 : Úrhellis rigning : Torrential rain shower 362 : Slydduél : Light sleet showers 365 : Dálítil eða mikil slydduél : Moderate or heavy sleet showers 368 : Lítilshátta Snjóél : Light snow showers 371 : Dálítil eða mikil snjóél : Moderate or heavy snow showers 386 : Lítilsháttar skúrir með þrumuveðri á köflum : Patchy light rain with thunder 389 : Dálítil eða mikil rigning með þrumuveðri : Moderate or heavy rain with thunder 392 : Lítilsháttar él með þrumuveðri á köflum : Patchy light snow with thunder 395 : Dálítil eða mikill snjórkoma með þrumuveðri : Moderate or heavy snow with thunder ================================================ FILE: share/translations/it-help.txt ================================================ Istruzioni: $ curl wttr.in # Il tempo nella tua posizione attuale $ curl wttr.in/muc # Meteo all'aeroporto di Monaco Tipi di località supportate: /paris # Il nome di una città /~Torre+Eiffel # Il nome di qualsiasi sito famoso /Москва # Il nome Unicode di qualsiasi sito in qualsiasi lingua /muc # Il codice di un aeroporto (3 lettere) /@stackoverflow.com # Il nome di un dominio web /94107 # Un prefisso /-78.46.106.79 # Coordinate GPS Siti speciali: /moon # La fase lunare (aggiungere ,+US o ,+France per questi paesi) /moon@2016-10-25 # La fase lunare in una data specifica (@2016-10-25) Unità: ?m # Metrico (SI) (lo standard ovunque tranne che negli Stati Uniti) ?u # Sistema unificato di classificazione del suolo o USCS (lo standard negli Stati Uniti) ?M # Mostra la velocità del vento in m/s Opzioni di visualizzazione: ?0 # Solo meteo attuale ?1 # Meteo attuale + previsioni per il giorno successivo ?2 # Il tempo attuale + le previsioni per i prossimi due giorni ?A # ignora l'agente utente e forza il formato di output ANSI (terminale) ?F # Non mostra la riga "Segui". ?n # Versione breve (solo giorno e notte) ?q # Versione silenziosa (senza il testo "Previsioni meteo:") ?Q # Versione super silenziosa (né "Previsioni meteo:" né nome della città) ?T # Modalità testuale (nessun colore) Opzioni utili per il formato PNG: /paris.png # Genera un'immagine PNG ?p # Aggiunge un bordo attorno all'immagine ?t # Trasparenza = 150 transparency=150 # Trasparenza da 0 a 255 (255 = nessuna trasparenza) Le opzioni possono essere utilizzate insieme: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # In modalità PNG le opzioni sono specificate dopo il carattere _ (trattino basso) anziché ? /Rome_0pq_lang=it.png # In modalità PNG usare opzioni brevi e lunghe, ma separate con _ (underscore) anziché ? e & Posizione: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accetta lingua: fr" wttr.in/paris Lingue supportate: FULL_TRANSLATION (supportato) PARTIAL_TRANSLATION (in corso) URL speciali: /:aiuto # Mostra questa pagina /:bash.function # Visualizza una funzione wttr() suggerita in bash /:translation # Visualizza le informazioni sui traduttori ================================================ FILE: share/translations/it.txt ================================================ : Pioggia intensa e grandine con temporali : Heavy rain and hail with thunderstorm : Pioggia intensa con temporale : Heavy rain with thunderstorm : Pioggia leggera e grandine con temporali : Light rain and hail with thunderstorm : Pioggia leggera e nevicata : Light rain and snow shower : Pioggia leggera con temporale : Light rain with thunderstorm : Leggera nevicata : Light snow shower : Nebbia parziale : Partial fog : Pioggia e grandine con temporali : Rain and hail with thunderstorm : Pioggia con temporale : Rain with thunderstorm : Nebbia leggera : Shallow fog : Foschia : Smoke : Burrasche : Squalls : Temporali nelle vicinanze : Thunderstorm in vicinity : Neve : Snow : Pioggia : Rain : Pioggia leggera, Rovesci di pioggia : Light Rain, Rain Shower : Rovesci di pioggia : Rain Shower : Banchi di nebbia : Patches of fog : Pioggerellina : Drizzle : Pioggerellina leggera : Light drizzle : Bassi cumuli di neve : Low drifting snow : Pioggia leggera e neve : Light rain and snow : Rovesci nelle vicinanze : Shower in vicinity : Pioggia con temporale : Rain with thunderstorm : Rovesci di pioggia e neve : Rain and snow shower : Temporale : Thunderstorm : Pioggerellina e pioggia : Drizzle and rain : Grandine con temporale : Hail with thunderstorm : Foschia : Haze : Pioggerellina leggera e pioggia : Light drizzle and rain : Pioggia leggera e piccoli cumuli di grandine/neve con temporale : Light rain and small hail/snow pallets with thunderstorm : Pioggia a tratti nelle vicinanze : Patchy rain nearby 113: Sereno : Clear 113: Soleggiato : Sunny 116: Parzialmente nuvoloso : Partly cloudy 119: Nuvoloso : Cloudy 122: Coperto : Overcast 143: Nebbia : Mist 176: Possibile pioggia a zone : Patchy rain possible 179: Possibili nevicate a zone : Patchy snow possible 182: Possibile nevischio a zone : Patchy sleet possible 185: Possibile pioviggine ghiacciata : Patchy freezing drizzle possible 200: Possibili violenti temporali improvvisi : Thundery outbreaks possible 227: Neve a raffiche : Blowing snow 230: Bufera di neve : Blizzard 248: Nebbia : Fog 260: Nebbia gelata : Freezing fog 263: Pioggerellina leggera a zone : Patchy light drizzle 266: Pioggerellina leggera : Light drizzle 281: Pioggia gelata : Freezing drizzle 284: Pioviggine ghiacciata : Heavy freezing drizzle 293: Pioggia leggera a zone : Patchy light rain 296: Pioggia leggera : Light rain 299: Pioggia moderata irregolare : Moderate rain at times 302: Pioggia moderata : Moderate rain 305: Pioggia forte irregolare : Heavy rain at times 308: Pioggia forte : Heavy rain 311: Pioggia leggera ghiacciata : Light freezing rain 314: Pioggia gelata moderata o pesante : Moderate or heavy freezing rain 317: Nevischio leggero : Light sleet 320: Nevischio moderato o pesante : Moderate or heavy sleet 323: Neve leggera a zone chiare : Patchy light snow 326: Neve leggera : Light snow 329: Neve moderata a zone : Patchy moderate snow 332: Neve moderata : Moderate snow 335: Abbondante nevicata a zone : Patchy heavy snow 338: Abbondante nevicata : Heavy snow 350: Grandine : Ice pellets 353: Pioggia leggera a scrosci : Light rain shower 356: Pioggia moderata o intensa a scrosci : Moderate or heavy rain shower 359: Piogge torrenziali a scrosci : Torrential rain shower 362: Nevischio leggero a colpi : Light sleet showers 365: Rovesci di pioggia moderati o pesanti : Moderate or heavy sleet showers 368: Deboli rovesci di neve : Light snow showers 371: Rovesci di neve moderati o abbondanti : Moderate or heavy snow showers 386: Pioggia leggera con tuoni e fulmini : Patchy light rain with thunder 389: Pioggia moderata o intensa con tuoni e fulmini : Moderate or heavy rain with thunder 392: Neve leggera a zone con tuoni : Patchy light snow with thunder 395: Neve moderata o pesante con tuoni : Moderate or heavy snow with thunder ================================================ FILE: share/translations/ja.txt ================================================ 113: 快晴 : Clear 113: 晴れ : Sunny 116: 所により曇り : Partly cloudy 119: 曇り : Cloudy 122: 曇り : Overcast 143: 靄 : Mist 176: 所により雨 : Patchy rain possible 179: 所により雪 : Patchy snow possible 182: 所により凍雨 : Patchy sleet possible 185: 所により着氷性の霧雨 : Patchy freezing drizzle possible 200: 雷雨の発生 : Thundery outbreaks possible 227: 吹雪 : Blowing snow 230: 猛吹雪 : Blizzard 248: 霧 : Fog 260: 着氷性の霧 : Freezing fog 263: 所により弱い霧雨 : Patchy light drizzle 266: 弱い霧雨 : Light drizzle 281: 着氷性の霧雨 : Freezing drizzle 284: 強い着氷性の霧雨 : Heavy freezing drizzle 293: 所により弱い雨 : Patchy light rain 296: 弱い雨 : Light rain 299: 時々雨 : Moderate rain at times 302: 雨 : Moderate rain 305: 時々強い雨 : Heavy rain at times 308: 強い雨 : Heavy rain 311: 弱い着氷性の雨 : Light freezing rain 314: 着氷性の雨または強い着氷性の雨 : Moderate or heavy freezing rain 317: 弱い凍雨 : Light sleet 320: 凍雨または強い凍雨 : Moderate or heavy sleet 323: 所により弱い雪 : Patchy light snow 326: 弱い雪 : Light snow 329: 所により雪 : Patchy moderate snow 332: 雪 : Moderate snow 335: 所により強い雪 : Patchy heavy snow 338: 強い雪 : Heavy snow 350: 凍雨 : Ice pellets 353: 弱い驟雨 : Light rain shower 356: 驟雨または強い驟雨 : Moderate or heavy rain shower 359: 激しい驟雨 : Torrential rain shower 362: 弱い凍雨 : Light sleet showers 365: 凍雨または強い凍雨 : Moderate or heavy sleet showers 368: 弱い驟雪 : Light snow showers 368: 弱い雨と驟雪と靄 : Light Rain And Snow Shower, Mist 371: 驟雪または強い驟雪 : Moderate or heavy snow showers 386: 所により雷を伴う弱い雨 : Patchy light rain with thunder 389: 雷を伴う雨または強い雨 : Moderate or heavy rain with thunder 392: 所により雷を伴う弱い雪 : Patchy light snow with thunder 395: 雷を伴う雪または強い雪 : Moderate or heavy snow with thunder ================================================ FILE: share/translations/kk-help.txt ================================================ Использование: $ curl wttr.in # текущее местоположение $ curl wttr.in/svo # погода в аэропорту Шереметьево (код ICAO: SVO) Поддерживаемые типы местоположений: /paris # город /~Eiffel+tower # любое местоположение /Москва # юникодное имя любого местоположения на любом языке /muc # код аэропорта ICAO (3 буквы) /@stackoverflow.com # доменное имя /94107 # почтовый индекс (только для США) /-78.46,106.79 # GPS-координаты Специальные условные местоположения: /moon # Фаза Луны (добавьте ,+US или ,+France для города Moon в США/Франции) /moon@2016-10-25 # Фаза Луны для указанной даты (@2016-10-25) Единицы измерений: ?m # метрические (СИ) (используются везде кроме США) ?u # USCS (используются в США) ?M # показывать скорость ветра в м/с Опции отображения: ?0 # только текущая погода ?1 # погода сегодня + 1 день ?2 # погода сегодня + 2 дня ?n # узкая версия (только день и ночь) ?q # тихая версия (без текста "Прогноз погоды") ?Q # сверхтихая версия (без "Прогноз погоды", нет названия города) ?T # отключить терминальные последовательности (без цветов) PNG-опции: /paris.png # сгенерировать PNG-файл ?p # добавить рамочку вокруг ?t # transparency=150 (прозрачность 150) transparency=... # прозрачность от 0 до 255 (255 = не прозрачный) Опции можно комбинировать: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # в PNG-запросах опции указываются после _ /Rome_0pq_lang=it.png # длинные опции разделяются знаком подчёркивания _ Локализация: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Поддерживаемые языки: FULL_TRANSLATION (поддерживаются) PARTIAL_TRANSLATION (в процессе) Специальные страницы: /:help # показать эту страницу /:bash.function # показать рекомендованную функцию wttr() /:translation # показать список переводчиков wttr.in ================================================ FILE: share/translations/kk.txt ================================================ 113: Ашық : Clear : Ясно 113: Шуақ күн : Sunny : Солнечно 116: Көшпелі бұлт : Partly cloudy : Переменная облачность 119: Күн бұлтты : Cloudy : Облачно 122: Бұлыңғыр күн : Overcast : Пасмурно 143: Мұнар : Mist : Дымка 176: Кей жерлерде жаңбыр жауады : Patchy rain possible : Местами дождь 179: Кей жерлерде кар жауады : Patchy snow possible : Местами снег 182: Кей жерлерде қар және жауын аралас : Patchy sleet possible : Местами дождь со снегом 185: Кей жерлерде үсік сіркіреме : Patchy freezing drizzle possible : Местами замерзающая морось 200: Кей жерлерде найзағай : Thundery outbreaks possible : Местами грозы 227: Жаяу бұрқасын : Blowing snow : Поземок 230: Бұрқасын : Blizzard : Метель 248: Тұман : Fog : Туман 260: Аса суықтау тұман : Freezing fog : Переохлажденный туман 263: Кей жерлерде әлсіз аяз : Patchy light drizzle : Местами слабая морось 266: Әлсіз аяз : Light drizzle : Слабая морось 281: Қақаған аяз : Freezing drizzle : Замерзающая морось 284: Қатты қақаған аяз : Heavy freezing drizzle : Сильная замерзающая морось 293: Кей жерлерде аз ғана жаңбыр : Patchy light rain : Местами небольшой дождь 296: Аздаған жаңбыр : Light rain : Небольшой дождь 299: Орташа жауын кезеңдері : Moderate rain at times : Временами умеренный дождь 302: Орташа жауын : Moderate rain : Умеренный дождь 305: Қатты жауын кезеңдері : Heavy rain at times : Временами сильный дождь 308: Қатты жаңбыр : Heavy rain : Сильный дождь 311: Әлсіз суықтау жаңбыр : Light freezing rain : Слабый переохлажденный дождь 314: Орташа немесе қатты суықтау жаңбыр : Moderate or heavy freezing rain : Умеренный или сильный переохлажденный дождь 317: Аздаған жаңбыр аралас қар : Light sleet : Небольшой дождь со снегом 320: Орташа немесе қатты жаңбыр аралас қар : Moderate or heavy sleet : Умеренный или сильный дождь со снегом 323: Кей жерлерде аздаған қар : Patchy light snow : Местами небольшой снег 326: Аздаған қар : Light snow : Небольшой снег 329: Кей жерлерде орташа жауған қар : Patchy moderate snow : Местами умеренный снег 332: Орташа жауған қар : Moderate snow : Умеренный снег 335: Кей жерлерде қатты жауған қар : Patchy heavy snow : Местами сильный снег 338: Қатты жауған қар : Heavy snow : Сильный снег 350: Мұздай жаңбыр : Ice pellets : Ледяной дождь 353: Аздаған нөсер жауын : Light rain shower : Небольшой ливневый дождь 356: Орташа немесе қатты нөсер жауын : Moderate or heavy rain shower : Умеренный или сильный ливневый дождь 359: Өте қатты нөсер жауын : Torrential rain shower : Очень сильный ливень 362: Аздаған нөсер жауын аралас қар : Light sleet showers : Небольшой ливневый дождь со снегом 365: Аздаған қар : Moderate or heavy sleet showers : Небольшой снег 368: Аздаған нөсер жауын аралас қар : Light snow showers : Небольшой ливневый дождь со снегом 371: Орташа немесе қатты қарлы нөсер : Moderate or heavy snow showers : Умеренный или сильный снежный ливень 386: Кей жерлерде шамалы найзағайлы жауын : Patchy light rain with thunder : Местами небольшой дождь с грозой 389: Орташа немесе қатты найзағайлы нөсер : Moderate or heavy rain with thunder : Умеренный или сильный дождь с грозой 392: Кей жерлерде шамалы қар және найзағай : Patchy light snow with thunder : Местами небольшой снег с грозой 395: Орташа немесе қатты қар және найзағай : Moderate or heavy snow with thunder : Умеренный или сильный снег с грозой ================================================ FILE: share/translations/lt-help.txt ================================================ Naudojimas: $ curl wttr.in # dabartinė vietovė $ curl wttr.in/plq # oras Palangos oro uoste Palaikomos vietovių rūšys: /panemunė # miesto pavadinimas /~Eiffel+tower # bet kuri vietovė (+ vietoj tarpų) /Магілёў # bet kurios vietovės pavadinimas Unikodu /plq # oro uosto kodas (3 raidės) /@stackoverflow.com # domeno vardas /94107 # pašto kodas (tik JAV) /-78.46,106.79 # GPS koordinatės Mėnulio fazių informacija: /moon # Mėnulio fazė (pridėkite ,+US arba +,France šio pavadinimo miestams) /moon@2016-10-25 # Mėnulio fazė datai (@2016-10-25) Matai: ?m # metrai (SI) (pagal nutylėjimą, naudojama visu išskyrus JAV) ?u # USCS (pagal nutylėjimą, naudojama JAV) ?M # vėjo greitis m/s Rodymo parinktys: ?0 # tik faktiniai orai ?1 # faktiniai orai + šiandienos prognozė ?2 # faktiniai orai + šiandienos + rytojaus prognozės A # ignoruoti naudotojo agentą (User-Agent) ir priverstinai formatuoti išvestį į ANSI (terminale) F # nerodyti eilutės apie atnaujinimų sekimą n # siaura versija (tik diena ir naktis) q # tylesnė versija (be teksto „Orų prognozė“) Q # labai tyli versija (be teksto „Orų prognozė“ ir be vietovės pavadinimo T # išjungti terminalo sekas (be spalvų) PNG parinktys: /panemunė.png # sukurti PNG failą p # apvesti išvestį rėmeliu t # skaidrumas 150 transparency=... # skaidrumas nuo 0 iki 255 (255 = neskaidrus) background=... # fono spalva RRGGBB forma, pvz., 00aaaa Parinktis galima jungti: /Panemunė?0pq /Panemunė?0pq&lang=lt /Panemunė_0pq.png # PNG failo pobūdis nurodomas po _ /Rēzekne_0pq_lang=lv.png # ilgavardės parinktys atskiriamos apatiniu brūkšniu Kalbos: $ curl lt.wttr.in/Panemunė $ curl wttr.in/panemunė?lang=lt $ curl -H "Accept-Language: lt" wttr.in/panemunė Palaikomos kalbos FULL_TRANSLATION (išverstos) PARTIAL_TRANSLATION (tebeverčiamos) Ypatingi URL: /:help # rodyti šį puslapį /:bash.function # rodyti rekomenduojamą bash funkciją wttr() /:translation # rodyti informaciją apie vertėjus ================================================ FILE: share/translations/lt.txt ================================================ 113: Giedra : Clear : 113: Saulėta : Sunny : 116: Nepastoviai debesuota : Partly cloudy : 119: Debesuota su pragiedruliais : Cloudy : 122: Debesuota : Overcast : 143: Migla : Mist : 176: Galimas silpnas lietus : Patchy rain possible : 179: Galimas nedidelis snygis : Patchy snow possible : 182: Galima nedidelė šlapdriba : Patchy sleet possible : 185: Galima nedidelė lijundra : Patchy freezing drizzle possible : 200: Spėjama perkūnija : Thundery outbreaks possible : 227: Pustymas : Blowing snow : 230: Pūga : Blizzard : 248: Rūkas : Fog : 260: Šarma : Freezing fog : 263: Protarpiais dulksna : Patchy light drizzle : 266: Dulksna : Light drizzle : 281: Lijundra : Freezing drizzle : 284: Stipri lijundra : Heavy freezing drizzle : 293: Protarpiais silpnas lietus : Patchy light rain : 296: Silpnas lietus : Light rain : 299: Protarpiais lietus : Moderate rain at times : 302: Lietus : Moderate rain : 305: Protarpiais stiprus lietus : Heavy rain at times : 308: Stiprus lietus : Heavy rain : 311: Silpna lijundra : Light freezing rain : 314: Vidutinė arba stipri lijundra : Moderate or heavy freezing rain : 317: Lengva šlapdriba : Light sleet : 320: Vidutinė arba stipri šlapdriba : Moderate or heavy sleet : 323: Protarpiais lengvas snygis : Patchy light snow : 326: Lengvas snygis : Light snow : 329: Protarpiais vidutinis snygis : Patchy moderate snow : 332: Snygis : Moderate snow : 335: Protarpiais stiprus snygis : Patchy heavy snow : 338: Stiprus snygis : Heavy snow : 350: Kruša : Ice pellets : 353: Nesmarki liūtis : Light rain shower : 356: Vidutinė arba smarki liūtis : Moderate or heavy rain shower : 359: Smarki liūtis : Torrential rain shower : 362: Protarpiais lengva šlapdriba : Light sleet showers : 365: Protarpiais vidutinė/smarki šlapdriba : Moderate or heavy sleet showers : 368: Protarpiais lengvas snygis : Light snow showers : 371: Protarpiais vidutinis/sunkus snygis : Moderate or heavy snow showers : 386: Protarpiais lengvas lietūs su perkūnija : Patchy light rain with thunder : 389: Vidutinis/sunkus lietus su perkūnija : Moderate or heavy rain with thunder : 392: Protarpiais lengvas snygis su perkūija : Patchy light snow with thunder : 395: Vidutinis/sunkus snygis su perkūnija : Moderate or heavy snow with thunder : ================================================ FILE: share/translations/lv-help.txt ================================================ Izmantošana: $ curl wttr.in # pašreizējā atrašanās vieta $ curl wttr.in/rix # laikapstākļi Rīgas lidostā Atbalstītie atrašanās vietu veidi: /paris # pilsētas nosaukums /~Eiffel+tower # jebkura atrašanās vieta (+ atstarpju vietā) /Москва # jebkuras atašanās vietas Unicode nosaukums jebkurā valodā /rix # lidostas kods (3 burti) /@stackoverflow.com # domēna vārds /94107 # pasta indekss (tikai ASV) /-78.46,106.79 # GPS koordinātas Informācija par Mēness fāzēm: /moon # Mēness fāze (jāpievieno ,+US vai ,+France noteiktai pilsētai) /moon@2016-10-25 # Mēness fāze datumā (@2016-10-25) Mērvienības: m # Metriskā sistēma (SI) (pēc noklusējuma izmanto visur izņemot ASV) u # USCS (pēc noklusējuma izmanto ASV) M # rādīt vēja ātrumu ar m/s Skata iespējas: 0 # tikai pašreizējie laikapstākļi 1 # pašreizējie laikapstākļi un šodienas prognoze 2 # pašreizējie laikapstākļi un šodienas un rītdienas prognoze A # neņemt vērā User-Agent un uzspiest ANSI izvades veidolu (terminālis) d # ierobežot izvadi ar standarta konsoles fonta glifiem F # nerādīt sekošanas rindu n # šaurā versija (tikai diena un nakts) q # saīsinātā versija (bez teksta "Laikapstākļi") Q # īpaši saīsinātā versija (bez teksta "Laikapstākļi" un pilsētas nosaukuma) T # izslēgt termināļa virknes (bez krāsām) PNG iespējas: /paris.png # izveidot PNG datni p # izvadei pievienot rāmi t # caurspīdīgums 150 transparency=... # caurspīdīgums no 0 - 255 (255 = necaurspīdīgs) background=... # fona krāsa veidolā RRGGBB, piem. 00aaaa Iespējas var apvienot: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNG datnes nosaukumā iespējas tiek rakstītas aiz _ /Rome_0pq_lang=it.png # garās iespējas tiek atadlītas ar apakšsvītru Lokalizācija: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Atbalstītās valodas: FULL_TRANSLATION (atbalstītas) PARTIAL_TRANSLATION (pie tām tiek strādāts) Īpaši URL: /:help # parāda šo lapu /:bash.function # parāda ieteikto bash funkciju wttr() /:translation # parāda informāciju par tulkotājiem ================================================ FILE: share/translations/lv.txt ================================================ : Tuvumā īslaicīgs lietus : Patchy rain nearby : Tuvumā pērkona negaiss : Thunderstorm in vicinity 113: Skaidrs : Clear 113: Saulains : Sunny 116: Daļēji mākoņains : Partly cloudy 119: Mākoņains : Cloudy 122: Apmācies : Overcast 143: Dūmaka : Mist 176: Vietām iespējams lietus : Patchy rain possible 179: Vietām iespējams sniegs : Patchy snow possible 182: Vietām iespējams sals : Patchy sleet possible 185: Vietām iespējama sasalstoša smidzināšana : Patchy freezing drizzle possible 200: Iespējams pērkona negaiss : Thundery outbreaks possible 227: Sniegputenis : Blowing snow 230: Sniega vētra : Blizzard 248: Migla : Fog 260: Sasalstoša migla : Freezing fog 263: Vietām viegla smidzināšana : Patchy light drizzle 266: Viegla smidzināšana : Light drizzle 281: Sasalstoša smidzināšana : Freezing drizzle 284: Spēcīga sasalstoša smidzināšana : Heavy freezing drizzle 293: Vietām viegls lietus : Patchy light rain 296: Viegls lietus : Light rain 299: Īslaicīgs mērens lietus : Moderate rain at times 302: Mērens lietus : Moderate rain 305: Īslaicīgs spēcīgs lietus : Heavy rain at times 308: Spēcīgs lietus : Heavy rain 311: Viegls sasalstošs lietus : Light freezing rain 314: Mērens vai spēcīgs un sasalstošs lietus : Moderate or heavy freezing rain 317: Viegls slapjdraņķis : Light sleet 320: Mērens vai spēcīgs slapjdraņķis : Moderate or heavy sleet 323: Vietām viegls sniegs : Patchy light snow 326: Viegls sniegs : Light snow 329: Vietām mērens sniegs : Patchy moderate snow 332: Mērens sniegs : Moderate snow 335: Vietām spēcīgs sniegs : Patchy heavy snow 338: Spēcīgs sniegs : Heavy snow 350: Krusa : Ice pellets 353: Vieglas lietusgāzes : Light rain shower 356: Mērenas vai spēcīgas lietusgāzes : Moderate or heavy rain shower 359: Spēcīgas lietusgāzes : Torrential rain shower 362: Vieglas slapjdraņķa gāzes : Light sleet showers 365: Mērens vai spēcīgs sniegs : Moderate or heavy sleet showers 368: Vieglas sniega gāzes : Light snow showers 371: Mērenas vai spēcīgs sniega gāzes : Moderate or heavy snow showers 386: Vietām viegls lietus ar pērkona negaisu : Patchy light rain with thunder 389: Mērens vai spēcīgs lietus ar pērkona negaisu : Moderate or heavy rain with thunder 392: Vietām viegls sniegs ar pērkona negaisu : Patchy light snow with thunder 395: Mērens vai spēcīgs sniegs ar pērkona negaisu : Moderate or heavy snow with thunder ================================================ FILE: share/translations/messages/en.yaml ================================================ lang: name: English code: "en" issue: "" translators: - "Igor Chubin @igor_chubin" locale: "en_US" translated: help: true weather: true messages: true messages: caption: 'Weather report for:' location: Location unknown_location: Unknown location capacity_limit_reached: |2 Sorry, we are running out of queries to the weather service at the moment. Here is the weather report for the default city (just to show you what it looks like). We will get new queries as soon as possible. You can follow https://twitter.com/igor_chubin for the updates. ====================================================================================== follow_me: |- Follow \\e[46m\\e[30m@igor_chubin\\e[0m for wttr.in updates new_feature: |- New feature: multilingual location names \\e[92mwttr.in/станция+Восток\\e[0m (in UTF-8) and location search \\e[92mwttr.in/~Kilimanjaro\\e[0m (just add ~ before) not_found_message: |2 We were unable to find your location so we have brought you to Oymyakon, one of the coldest permanently inhabited locales on the planet. views: v1: morning: "Morning" noon: "Noon" evening: "Evening" night: "Night" v2: weather_report_for: "Weather report for:" weather: "Weather" timezone: "Timezone" now: "Now" dawn: "Dawn" sunrise: "Sunrise" zenith: "Zenith" sunset: "Sunset" dusk: "Dusk" ================================================ FILE: share/translations/messages/gu.yaml ================================================ lang: name: Gujarati code: gu issue: "774" translators: - @wylited (on GitHub) locale: gu_IN translated: help: false weather: false messages: false messages: caption: 'Weather report for:' location: Location unknown_location: Unknown location capacity_limit_reached: |2 Sorry, we are running out of queries to the weather service at the moment. Here is the weather report for the default city (just to show you what it looks like). We will get new queries as soon as possible. You can follow https://twitter.com/igor_chubin for the updates. ====================================================================================== follow_me: |- Follow \\e[46m\\e[30m@igor_chubin\\e[0m for wttr.in updates new_feature: |- New feature: multilingual location names \\e[92mwttr.in/станция+Восток\\e[0m (in UTF-8) and location search \\e[92mwttr.in/~Kilimanjaro\\e[0m (just add ~ before) not_found_message: |2 We were unable to find your location so we have brought you to Oymyakon, one of the coldest permanently inhabited locales on the planet. views: v1: morning: "Morning" noon: "Noon" evening: "Evening" night: "Night" v2: weather_report_for: "Weather report for:" weather: "Weather" timezone: "Timezone" now: "Now" dawn: "Dawn" sunrise: "Sunrise" zenith: "Zenith" sunset: "Sunset" dusk: "Dusk" ================================================ FILE: share/translations/mg-help.txt ================================================ Fampiasana azy: $ curl wttr.in # toetr'andro eo amin'ny toerana misy anao $ curl wttr.in/antananarivo # totr'andro any Antananarivo Karazana toerana azo ampesaina: /fianarantsoa # nom de la ville /~Eiffel+tower # anaran-toerana rehetra /Москва # anarana Unikody na anaran-toerana rehetra amin'ny fiteny rehetra /tnr # kaody ny seranam-piaramanidina (litera 3) /@stackoverflow.com # anarana domaina (rohy) /94107 # Kaody postaly (hoan'ny Etazonia iany) /-78.46,106.79 # coordonnées GPS Toerana somary miavaka: /moon # Dignana ny volana(ampio ,+US ou ,+France raha toa ka misy toerana mitondra anio anarana io) /moon@2016-10-25 # Dignana ny volana hoan'ny daty iray(@2016-10-25) Refy: ?m # rafitra metrika (fampiasain'ny rehetra afatsy ny Amerika Avaratra) ?u # USCS (Fampiasan'ny Etazonia) ?M # mampiseho ny hafainganam-pandehan'ny rivotra amin'ny metatra isan-segondra Fomba fampisehoana: ?0 # androany fotsiny ?1 # androany sy rampitso ?2 # androany miampy roa andro ?n # kinova fohy (atoandro sy ariva fotsiny) ?q # kinova tsotra (tsisy "Vinavina ny totrandro androany") ?Q # version super-silencieuse (pas d'en-tête "Prévisions météo pour", pas de nom de la ville) ?T # séquences d'échappement pour terminaux désactivées (pas de couleurs) Fomba fampisehoana sary PNG: /antananarivo.png # mamoka sary PNG ?p # manisy kadra manodidina ilay seho mivoaka ?t # transparency 150 (fangaraharana 150) transparency=... # fangaraharana ao anatin'ny 0 atramin'ny 255 (255 = tsisy fangaraharana) Manambatra anireo safidy: /antananarivo?0pq /antananarivo?0pq&lang=mg /antananarivo_0pq.png # raha toa ka mampiasa fampisehoana aminn'ny sary PNG dia asina tsipik'ambany `_` manelanelana azy /Rome_0pq_lang=it.png # ireo safidy lava dia sarahina amin'ny tsipik'ambany `_` ian'ny koa Toerana: $ curl fr.wttr.in/antananarivo $ curl wttr.in/antananarivo?lang=mg $ curl -H "Accept-Language: mg" wttr.in/paris Langues supportées: FULL_TRANSLATION (Voadika teny tanteraka) PARTIAL_TRANSLATION (Voadika teny ampahany) URLs particulières: /:help # mampiseho ito pejy ito /:bash.function # sosokevitra fonction bash wttr() /:translation # mampahafantra ny momba ny fandikanteny ao amin'ny wttr.in ================================================ FILE: share/translations/mg.txt ================================================ : Oram-be manavandra sy oram-baratra : Heavy rain and hail with thunderstorm : Oram-be sy oram-baratra : Heavy rain with thunderstorm : Oram-baratra malefaka sy manavandra : Light rain and hail with thunderstorm : Orana malefaka sy ranomandry : Light rain and snow shower : Oranam-baratra malefaka : Light rain with thunderstorm : Orana malefaka ranomandry : Light snow shower : Zavona ampahany : Partial fog : Oram-baratra manavandra : Rain and hail with thunderstorm : Zavona malefaka : Shallow fog : Zavona : Smoke : Oram-baratra : Squalls : Orages proches : Thunderstorm in vicinity : Oram-panala : Snow : Orana : Rain : Orana malefaka : Light Rain, Rain Shower : Ranonorana : Rain Shower : Vongan-zavona : Patches of fog : Pitipitik'orana : Drizzle : Pitipitik'orana : Light drizzle : Oram-panala mitsoka ambany : Low drifting snow : Oram-panala malefaka : Light rain and snow : Oram-be manakaiky : Shower in vicinity : Oram-baratra : Rain with thunderstorm : Oram-be sy oram-panala : Rain and snow shower : Ora-mikija : Thunderstorm : Pitipitik'orana : Drizzle and rain : Oram-be manavandra : Hail with thunderstorm : Zavona : Haze : Zavona arahin'orana : Light drizzle and rain : Ora-mikija, orana malefaka et avandra / Oram-panala : Light rain and small hail/snow pallets with thunderstorm 113 : Tsara ny andro : Clear 113 : Masoandro mibalika : Sunny 116 : Rakodrahona ampahany : Partly cloudy 119 : Rakodrahona : Cloudy 122 : Manjombona : Overcast 143 : Zavona : Mist 176 : Mety hanorana matevina : Patchy rain possible 179 : Mety hisy oram-panala matevina : Patchy snow possible 182 : Mety hanerika kely : Patchy sleet possible 185 : Mety hisy zavona mandry sy matevina : Patchy freezing drizzle possible 200 : Mety hisy Ora-mikija : Thundery outbreaks possible 227 : Oram-panala : Blowing snow 230 : Tafiotran’orampanala : Blizzard 248 : Zavona : Fog 260 : Zavona mangatsiaka : Freezing fog 263 : Zavona matevina : Patchy light drizzle 266 : Zavona malefakaa : Light drizzle 281 : Zavona mandry : Freezing drizzle 284 : Zavona mangatsika mafy : Heavy freezing drizzle 293 : Orana matevina : Patchy light rain 296 : Orana malefaka : Light rain 299 : Orana malefaka miverimberina : Moderate rain at times 302 : Orana malefaka : Moderate rain 305 : Oram-be miverimberina : Heavy rain at times 308 : Oram-be : Heavy rain 311 : Orana mangatsiaka : Light freezing rain 314 : Orana malefaka na mafy mangatsiaka : Moderate or heavy freezing rain 317 : Tafiotran’orampanala maivana : Light sleet 320 : Tafiotran’orampanala malefaka na mafy : Moderate or heavy sleet 323 : Oram-panala matevina : Patchy light snow 326 : Oram-panala maivana : Light snow 329 : Oram-panala malefaka : Patchy moderate snow 332 : Latsakoram-panala malefaka : Moderate snow 335 : Oram-panala mavesatra sy matevina : Patchy heavy snow 338 : Oram-panala mafy : Heavy snow 350 : Pitipitika ranomandro : Ice pellets 353 : Oram-be malefaka : Light rain shower 356 : Orana antoniny sy mavesatra : Moderate or heavy rain shower 359 : Oram-be mikija : Torrential rain shower 362 : Pitipitik'oram-panala mikija : Light sleet showers 365 : Pitipitik'oram-panala malefaka na mavesatra : Moderate or heavy sleet showers 368 : Pitipitik'oram-panala maivana : Light snow showers 371 : Rotsak'oram-panala maivana sy mavesatra : Moderate or heavy snow showers 386 : Oram-baratra matevina : Patchy light rain with thunder 389 : Oram-baratra malefaka na mavesatra : Moderate or heavy rain with thunder 392 : Rotsak'oram-panala maivana arahim-baratra : Patchy light snow with thunder 395 : Rotsak'oram-panala malefaka na mavesatra arahim-baratra : Moderate or heavy snow with thunder ================================================ FILE: share/translations/mk.txt ================================================ 113: Ведро: Clear 113: Сончево: Sunny 116: Делумно Облачно: Partly cloudy 119: Облачно: Cloudy 122: Облачно: Overcast 143: Слаба магла: Mist 176: Умерено врнежливо во близина: Patchy rain nearby 176: Умерено врнежливо можно: Patchy rain possible 179: Умерено снежливо можно: Patchy snow possible 182: Умерено можно: Patchy sleet possible 185: Умерено: Patchy freezing drizzle possible 200: Грмежи во близина: Thundery outbreaks in nearby 200: Грмежи можно: Thundery outbreaks possible 227: Снег: Blowing snow 230: Виулица: Blizzard 248: Магла: Fog 260: Смрзнувачка магла: Freezing fog 263: Умерен слаб дожд: Patchy light drizzle 266: Роса: Light drizzle 281: Смрзнувачка роса: Freezing drizzle 284: Тежка смрзнувачка роса: Heavy freezing drizzle 293: Умерен слаб дожд: Patchy light rain 296: Слаб дожд: Light rain 299: Умерен дожд понекогаш: Moderate rain at times 302: Умерен дожд: Moderate rain 305: Пороен дожд: Heavy rain at times 308: Пороен дожд : Heavy rain 311: Смрзнувачки дожд: Light freezing rain 314: Умерен или тешки врнежи со дожд: Moderate or heavy freezing rain 317: Дожд со лед: Light sleet 320: Умерен или тежок дожд со лед: Moderate or heavy sleet 323: Умерени врнежи на слаб снег: Patchy light snow 326: Слабо врнежи снег: Light snow 329: Умерени врнежи на снег: Patchy moderate snow 332: Умерен врнежи на снег: Moderate snow 335: Умерено тешки врнежи на снег: Patchy heavy snow 338: Тешки врнежи со снег: Heavy snow 350: Град: Ice pellets 353: Слаби врнежи: Light rain shower 356: Умерени или тешки врнежи: Moderate or heavy rain shower 359: Пороен дожд: Torrential rain shower 362: Слаби дождови: Light sleet showers 365: Умерено или тешки : Moderate or heavy sleet showers 368: Слаби врнежи снег: Light snow showers 371: Умерено или тежки врнежи со снег: Moderate or heavy snow showers 386: Умерено слаб дожд во области со грмежи: Patchy light rain in area with thunder 386: Умерено слаб дожд со грмежи: Patchy light rain with thunder 389: Умерен или тешки врнежи во области со грмежи: Moderate or heavy rain in area with thunder 389: Умерен или тешки врнежи со грмежи: Moderate or heavy rain with thunder 392: Умерено слаб снег со грмежи: Patchy light snow with thunder 395: Умерен или тешки врнежи снег во области со грмежи: Moderate or heavy snow in area with thunder 395: Умерен или тешки врнежи снег со грмежи: Moderate or heavy snow with thunder ================================================ FILE: share/translations/mr-help.txt ================================================ वापर: $ curl wttr.in # वर्तमान स्थळाचे हवामान $ curl wttr.in/muc # म्युनिक विमानतळावरील हवामान उपलब्धीत/प्रयोज्य स्थळांचे प्रकार: /paris # शहराचे नाव /~Eiffel+tower # कोणत्याही स्थळाचे नाव (रिकाम्या ठिकाणी (स्पेस ऐवजी) +) /Москва # युनिकोड स्वरूपात कोणत्याही भाषेतील कोणत्याही स्थळाचे नाव /muc # विमातळाचे संकेत (कोड) (३ अक्षरे) /@stackoverflow.com # संकेतस्थळाचे डोमेन नाव /94107 # क्षेत्र कोड /-78.46,106.79 # जीपीएस सहनिर्देशक (रेखांश, अक्षांश) चंद्राच्या कलेची माहिती: /moon # चंद्राची कला (विशिष्ट स्थळासाठी +US, +France इत्यादी जोडा) /moon@2016-10-25 # विशिष्ट दिनी चंद्राची कला (@2016-10-25) एकक: m # दशमान (मेट्रिक/SI) (अमेरिका वगळता सर्वत्र वापरली जाते) u # USCS (अमेरिकेत वापरली जाते) M # वाऱ्याचा वेग मीटर प्रति सेकंद (m/s) मध्ये दाखवा दृश्य पर्याय: 0 # केवळ वर्तमान हवामान 1 # वर्तमान + आजचा हवामान अंदाज 2 # वर्तमान + आजचा + उद्याचा हवामान अंदाज A # (टर्मिनल मध्ये) युसर-एजन्ट दुर्लक्षित करून एएनएसआय (ANSI) स्वरूप वापरा F # "अनुसरण करा" (फॉलो) ची ओळ अदृश्य करा n # अरुंद स्वरूप (फक्त दुपार व रात्र) q # शांत स्वरूप ("हवामान अंदाज" मजकूर अदृश्य) Q # अतिशांत स्वरूप ("हवामान अंदाज" मजकूर व शहराचे नाव अदृश्य) T # टर्मिनल सिक्वेन्स(क्रम) बंद (बेरंगीत) PNG पर्याय: /paris.png # PNG फाईल निर्माण करा p # प्रतिमेभोवती चौकट जोडा t # 150 पारदर्शकता transparency=... # 0 ते 255 पारदर्शकता (255 = अपारदर्शक) background=... # RRGGBB (लाल हिरवा निळा) स्वरूपात पार्श्वभूमीचा रंग, उदा. 00aaaa पर्याय एकत्र करू शकता: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNG फाईलच्या बाबतीत पर्याय अधोरेखे "_" नंतर लिहिले जातात /Rome_0pq_lang=it.png # लांब पर्याय हे अधोरेखेने विभाजित केले जातात भाषांतर: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris उपलब्ध भाषा: FULL_TRANSLATION (उपलब्ध) PARTIAL_TRANSLATION (काम चालू) विशेष दुवे: /:help # हे पृष्ठ दाखवा /:bash.function # शिफारस केलेले बॅश "wttr()" कार्य दाखवा /:translation # भाषांतर करणाऱ्यांची माहिती दाखवा ================================================ FILE: share/translations/mr.txt ================================================ : मुसळधार पाऊस, गारा व झंझावात : Heavy rain and hail with thunderstorm : मुसळधार पाऊस व झंझावात : Heavy rain with thunderstorm : हलका पाऊस, गारा व झंझावात : Light rain and hail with thunderstorm : हलका पाऊस व हिमवर्षाव : Light rain and snow shower : हलका पाऊस व झंझावात : Light rain with thunderstorm : हलका हिमवर्षाव : Light snow shower : आंशिक दाट धुके : Partial fog : पाऊस, गारा व झंझावात : Rain and hail with thunderstorm : पाऊस व झंझावात : Rain with thunderstorm : उथळ दाट धुके : Shallow fog : धूर : Smoke : चंडवात : Squalls : झंझावात जवळपास : Thunderstorm in vicinity : हिमवर्षाव : Snow : पाऊस : Rain : हलका पाऊस, पावसाच्या सरी : Light Rain, Rain Shower : पावसाच्या सरी : Rain Shower : तुरळक दाट धुके : Patches of fog : रिमझिम : Drizzle : हलका रिमझिम पाऊस : Light drizzle : कमी वाहणारे बर्फ : Low drifting snow : हलका पाऊस आणि बर्फ : Light rain and snow : पावसाची सर जवळपास : Shower in vicinity : पाऊस व झंझावात : Rain with thunderstorm : पाऊस व हिमवर्षाव : Rain and snow shower : झंझावात : Thunderstorm : रिमझिम व पाऊस : Drizzle and rain : गारा व झंझावात : Hail with thunderstorm : विरळ धुके : Haze : हलके रिमझिम व पाऊस : Light drizzle and rain : झंझावात सह हलका पाऊस व लहान गारा : Light rain and small hail/snow pallets with thunderstorm 113 : स्वच्छ : Clear 113 : ऊन : Sunny 116 : काहीसे ढगाळ : Partly cloudy 119 : ढगाळ : Cloudy 122 : मळभ : Overcast 143 : धुके : Mist 176 : तुरळक पावसाची शक्यता : Patchy rain possible 179 : तुरळक बर्फाची शक्यता : Patchy snow possible 182 : तुरळक हिमयुक्त पावसाची शक्यता : Patchy sleet possible 185 : तुरळक थंड रिमझिमची शक्यता : Patchy freezing drizzle possible 200 : झंझावाताची शक्यता : Thundery outbreaks possible 227 : वाहणारा बर्फ : Blowing snow 230 : हिमवादळ : Blizzard 248 : दाट धुके : Fog 260 : थंड दाट धुके : Freezing fog 263 : तुरळक रिमझिम : Patchy light drizzle 266 : हलके रिमझिम : Light drizzle 281 : थंड रिमझिम : Freezing drizzle 284 : अतिथंड रिमझिम : Heavy freezing drizzle 293 : तुरळक हलका पाऊस : Patchy light rain 296 : हलका पाऊस : Light rain 299 : अधूनमधून हलका पाऊस : Moderate rain at times 302 : मध्यम पाऊस : Moderate rain 305 : अधूनमधून मुसळधार पाऊस : Heavy rain at times 308 : मुसळधार पाऊस : Heavy rain 311 : हलका थंड पाऊस : Light freezing rain 314 : मध्यम ते अतिथंड पाऊस : Moderate or heavy freezing rain 317 : हलका हिमयुक्त पाऊस : Light sleet 320 : मध्यम ते अतिथंड हिमयुक्त पाऊस : Moderate or heavy sleet 323 : तुरळक हलका हिमवर्षाव : Patchy light snow 326 : हलका हिमवर्षाव : Light snow 329 : तुरळक मध्यम हिमवर्षाव : Patchy moderate snow 332 : मध्यम हिमवर्षाव : Moderate snow 335 : तुरळक जोरदार हिमवर्षाव : Patchy heavy snow 338 : जोरदार हिमवर्षाव : Heavy snow 350 : मऊ गारा : Ice pellets 353 : पावसाच्या हलक्या सरी : Light rain shower 356 : पावसाच्या मध्यम ते जोरात सरी : Moderate or heavy rain shower 359 : मुसळधार पावसाच्या सरी : Torrential rain shower 362 : हिमयुक्त पावसाच्या हलक्या सरी : Light sleet showers 365 : हिमयुक्त पावसाच्या मध्यम ते जोरात सरी : Moderate or heavy sleet showers 368 : बर्फाच्या हलक्या सरी : Light snow showers 371 : बर्फाच्या मध्यम ते जोरात सरी : Moderate or heavy snow showers 386 : तुरळक हलका पाऊस व गडगडाट : Patchy light rain with thunder 389 : मध्यम ते जोरात पाऊस व गडगडाट : Moderate or heavy rain with thunder 392 : तुरळक हलका बर्फ व गडगडाट : Patchy light snow with thunder 395 : मध्यम ते जोरात बर्फ व गडगडाट : Moderate or heavy snow with thunder ================================================ FILE: share/translations/nb-help.txt ================================================ Bruk: $ curl wttr.in # nåværende lokasjon $ curl wttr.in/osl # været på Gardermoen flyplass Støttede lokasjonsspesifiseringer: /oslo # bynavn /~Ullevål+stadion # valgfri lokasjon /Москва # Unicode navn på lokasjon på alle språk /osl # flyplasskode (3 bokstaver) /@stackoverflow.com # domenenavn /94107 # postnummer (bare USA) /-78.46,106.79 # GPS-koordinater Spesielle lokasjoner: /moon # Månefase (legg til ,+US eller ,+France disse byene) /moon@2016-10-25 # Månefase for spesifisert dato (@2016-10-25) Enheter: ?m # metrisk (SI) (standard alle steder unntatt USA) ?u # USCS (standard i USA) ?M # vindstyrke i meter per sekund Visningsvalg: ?0 # kun været nå ?1 # været nå + 1 dag ?2 # været nå + 2 dager ?n # smal visning (bare dag og natt) ?q # stille visning (ingen "Værmelding"-tekst) ?Q # superstille visning (ingen "Værmelding", ingen bynavn) ?T # ingen terminalsekvenser (ingen farger) PNG valg: /paris.png # generer en PNG-fil ?p # legg til ramme ?t # gjennomsiktighet 150 transparency=... # gjennomsiktighet fra 0 til 255 (255 = ikke gjennomsiktig) Tilvalg kan kombineres: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # for PNG er filmodus spesifisert etter _ /Rome_0pq_lang=it.png # lange tilvalg separeres med understrek Oversettelser: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Støttede språk: FULL_TRANSLATION (støttet) PARTIAL_TRANSLATION (under arbeid) Spesielle URLer: /:help # vis denne siden /:bash.function # vis den foreslåtte bash-funksjonen wttr() /:translation # vis informasjon om oversetterne ================================================ FILE: share/translations/nb.txt ================================================ 113: Sol : Sunny 114: Klart : Clear 116: Delvis skyet : Partly cloudy 119: Skyet : Cloudy 122: Overskyet : Overcast 143: Dis : Mist 176: Sjanser for skiftende regn : Patchy rain possible 179: Sjanser for skiftende snø : Patchy snow possible 182: Sjanser for skiftende sludd : Patchy sleet possible 185: Sjanser for skiftende, kaldt duskregn : Patchy freezing drizzle possible 200: Fare for torden : Thundery outbreaks possible 227: Snøfokk : Blowing snow 230: Snøstorm : Blizzard 248: Tåke : Fog 260: Kald tåke : Freezing fog 263: Skiftende, lett duskregn : Patchy light drizzle 266: Lett duskregn : Light drizzle 281: Kaldt duskregn : Freezing drizzle 284: Kraftig, kaldt duskregn : Heavy freezing drizzle 293: Skiftende, lett regn : Patchy light rain 296: Lett regn : Light rain 299: Til tider noe regn : Moderate rain at times 302: Noe regn : Moderate rain 305: Til tider kraftig regn : Heavy rain at times 308: Kraftig regn : Heavy rain 311: Lett, kaldt regn : Light freezing rain 314: Moderat eller kraftig kaldt regn : Moderate or heavy freezing rain 317: Lett sludd : Light sleet 320: Moderat eller kraftig sludd : Moderate or heavy sleet 323: Skiftende, lett snø : Patchy light snow 326: Lett snø : Light snow 329: Skiftende, moderat snø : Patchy moderate snow 332: Noe snø : Moderate snow 335: Skiftende, kraftig snøfall : Patchy heavy snow 338: Kraftig snøfall : Heavy snow 350: Hagl : Ice pellets 353: Lett regnskur : Light rain shower 356: Moderat eller tung regnskur : Moderate or heavy rain shower 359: Styrtregn : Torrential rain shower 362: Lette sluddbyger : Light sleet showers 365: Moderate eller tunge sluddbyger : Moderate or heavy sleet showers 368: Lette snøbyger : Light snow showers 371: Moderate eller kraftige snøbyger : Moderate or heavy snow showers 386: Skiftende, lett regn med torden : Patchy light rain with thunder 389: Moderat eller kraftig regn med torden : Moderate or heavy rain with thunder 392: Skiftende, lett snø med torden : Patchy light snow with thunder 395: Moderat eller kraftig snø med torden : Moderate or heavy snow with thunder : Dis i nærheten : Haze in vicinity : Kraftig regn- og snøbyge : Heavy rain and snow shower : Kraftig regnbyge : Heavy rain shower : Lett duskregn og snøkorn : Light drizzle and snow grains : Lett regn- og tåkebyge : Light rain and mist shower : Lett byge med små hagl : Light small hail shower : Lett byge med ukjent nedbør : Light unknown precipitation shower : Regn og små hagl med tordenvær : Rain and small hail with thunderstorm : Snøbyge i nærheten : Snow shower in vicinity : Tordenvær i nærheten : Thundery outbreaks nearby : Snøfokk nær bakken : Low drifting snow : Tåkebanker : Patches of fog : Duskregn : Drizzle : Lett duskregn og regn : Light drizzle and rain : Snø : Snow : Regn : Rain : Regnbyge : Rain shower : Sandfokk : Blowing sand : Utbredt støvfokk : Blowing widespread dust : Utbredt støvfokk i nærheten : Blowing widespread dust in vicinity : Duskregn og tåke : Drizzle and fog : Duskregn og regn : Drizzle and rain : Duskregn og regnbyge : Drizzle and rain shower : Duskregn med tordenvær : Drizzle with thunderstorm : Støvstorm : Dust storm : Tåke i nærheten : Fog in vicinity : Underkjølt duskregn og regn : Freezing drizzle and rain : Underkjølt tåke : Freezing mist : Underkjølt regn : Freezing rain : Underkjølt regn og snø : Freezing rain and snow : Underkjølt ukjent nedbør : Freezing unknown precipitation : Skydrage i nærheten : Funnel cloud in vicinity : Haglbyge : Hail shower : Hagl med tordenvær : Hail with thunderstorm : Dis : Haze : Kraftig snøfokk : Heavy blowing snow : Kraftig duskregn : Heavy drizzle : Kraftig duskregn og regn : Heavy drizzle and rain : Kraftig underkjølt ukjent nedbør : Heavy freezing unknown precipitation : Kraftig hagl med tordenvær : Heavy hail with thunderstorm : Kraftige ispellets : Heavy ice pellets : Kraftig regn- og haglbyge : Heavy rain and hail shower : Kraftig regn og hagl med tordenvær : Heavy rain and hail with thunderstorm : Kraftig byge med regn og små hagl : Heavy rain and small hail shower : Kraftig regn og små hagl med tordenvær : Heavy rain and small hail with thunderstorm : Kraftig regn og snø : Heavy rain and snow : Kraftig regn og snø med tordenvær : Heavy rain and snow with thunderstorm : Kraftig regn med tordenvær : Heavy rain with thunderstorm : Kraftig byge med små hagl : Heavy small hail shower : Kraftig snøbyge : Heavy snow shower : Kraftig snø med tordenvær : Heavy snow with thunderstorm : Iskrystaller : Ice crystals : Lett snøfokk : Light blowing snow : Lett duskregn og tåke : Light drizzle and fog : Lett duskregn og ispellets : Light drizzle and ice pellets : Lett duskregn, regn og snø : Light drizzle and rain and snow : Lett duskregn og regnbyge : Light drizzle and rain shower : Lett duskregn og regn med tordenvær : Light drizzle and rain with thunderstorm : Lett duskregn og snø : Light drizzle and snow : Lett duskregn med tordenvær : Light drizzle with thunderstorm : Lett underkjølt duskregn : Light freezing drizzle : Lett underkjølt duskregn og regn : Light freezing drizzle and rain : Lett underkjølt duskregn og snø : Light freezing drizzle and snow : Lett underkjølt tåke : Light freezing fog : Lett underkjølt regn og snø : Light freezing rain and snow : Lett underkjølt ukjent nedbør : Light freezing unknown precipitation : Lett haglbyge : Light hail shower : Lett iskrystaller : Light ice crystals : Lett ispellets : Light ice pellets : Lett regn og dis : Light rain and mist : Lett byge med små hagl : Light rain and small hail shower : Lett regn og små hagl med tordenvær : Light rain and small hail with thunderstorm : Lett regn og snø : Light rain and snow : Lett regn- og snøbyge : Light rain and snow shower : Lett regn med tordenvær : Light rain with thunderstorm : Lett snø og ispellets : Light snow and ice pellets : Lett snø og små hagl : Light snow and small hail : Lett snøbyge : Light snow shower : Lett snø med tordenvær : Light snow with thunderstorm : Lett tordenvær : Light thunderstorm : Delvis tåke : Partial fog : Tåkebanker i nærheten : Patches of fog in vicinity : Lokalt lett regn med tordenvær : Patchy light rain in area with thunder : Lokalt lett snø med tordenvær : Patchy light snow in area with thunder : Regn og haglbyge : Rain and hail shower : Regn og hagl med tordenvær : Rain and hail with thunderstorm : Regn og dis : Rain and mist : Regn og dis med tordenvær : Rain and mist with thunderstorm : Regn og snø : Rain and snow : Regn- og snøbyge : Rain and snow shower : Regn og snø med tordenvær : Rain and snow with thunderstorm : Regn i nærheten : Rain in vicinity : Regnbyge i nærheten : Rain shower in vicinity : Regn med tordenvær : Rain with thunderstorm : Sandstorm : Sandstorm : Lav tåke : Shallow fog : Lav dis : Shallow mist : Byge : Shower : Byge i nærheten : Shower in vicinity : Små hagl : Small hail : Byge med små hagl : Small hail shower : Små hagl med tordenvær : Small hail with thunderstorm : Snø og ispellets : Snow and ice pellets : Snø og tåke : Snow and mist : Snøbyge : Snow shower : Snø med tordenvær : Snow with thunderstorm : Vindkast : Squalls : Tordenvær : Thunderstorm : Ukjent nedbør : Unknown precipitation : Byge med ukjent nedbør : Unknown precipitation shower : Ukjent nedbør med tordenvær : Unknown precipitation with thunderstorm : Vulkansk aske : Volcanic ash : Vulkansk aske i nærheten : Volcanic ash in vicinity : Utbredt støv : Widespread dust ================================================ FILE: share/translations/nl-help.txt ================================================ Gebruik: $ curl wttr.in # huidige locatie $ curl wttr.in/muc # weer op de luchthaven van München Ondersteunde locatiesoorten: /paris # stadsnaam /~Eiffel+tower # elke locatie (+ for spaties) /Москва # Unicodenaam van elke locatie in elke taal /muc # vliegveldcode (3 letters) /@stackoverflow.com # domeinnaam /94107 # gebiedscode /-78.46,106.76 # GPS-coördinaten Maanstand informatie: /moon # Maanstand (voeg ,+US of ,+France toe voor deze plekken) /moon@2016-10-25 # Maanstand op deze datum (@2016-10-25) Eenheden: m # metriek (SI) (overal gebruikt als standaard behalve in US) u # USCS (standaard in US) M # laat windsnelheid in m/s zien Beeld opties: 0 # alleen huidig weer 1 # huidig weer + verwachting voor vandaag 2 # huidig weer + verwachting voor vandaag en morgen A # negeer User-Agent en forceer ANSI uitvoerformaat (terminal) F # Toon niet de "Follow" regel n # smalle versie (alleen dag en nacht) q # stille versie (geen "Weerbericht" tekst) Q # superstille versie (geen "Weerbericht", geen stadsnaam) T # schakel terminalcodes uit (geen kleur) PNG opties: /paris.png # genereert een PNG-bestand ?p # voegt een rand toe rond de uitvoer ?t # transparantie 150 transparency=... # transparantie van 0 to 255 (255 is ondoorzichtig) background=... # achtergrondkleur in formaat RRGGBB, bijv. 00aaaa Opties kunnen worden gecombineerd: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # in PNG het bestandstype specificeren achter _ /Rome_0pq_lang=it.png # lange opties worden gescheiden met _ Lokalisatie: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Ondersteunde talen: FULL_TRANSLATION (supported) PARTIAL_TRANSLATION (in progress) Speciale URLs: /:help # toon helppagina /:bash.function # toon voorgestelde wttr() bash-functie /:translation # toon informatie van de vertalers ================================================ FILE: share/translations/nl.txt ================================================ : Zware regen en hagel met onweer : Heavy rain and hail with thunderstorm : Zware regen met onweer : Heavy rain with thunderstorm : Lichte regen en hagel met onweer : Light rain and hail with thunderstorm : Lichte regen en sneeuwbui : Light rain and snow shower : Lichte regen met onweer : Light rain with thunderstorm : Lichte sneeuwbui : Light snow shower : Plaatselijke mist : Partial fog : Regen en hagel met onweer : Rain and hail with thunderstorm : Regen met onweer : Rain with thunderstorm : Lichte mist : Shallow fog : Nevel : Smoke : Windstoten : Squalls : Onweer in de buurt : Thunderstorm in vicinity : Sneeuw : Snow : Regen : Rain : Lichte regen, regenbuien : Light Rain, Rain Shower : Regenbuien : Rain Shower : Plaatselijke mist : Patches of fog : Motregen : Drizzle : Lichte motregen : Light drizzle : Laag stuifsneeuw : Low drifting snow : Lichte regen en sneeuw : Light rain and snow : Buien in de buurt : Shower in vicinity : Regen met onweer : Rain with thunderstorm : Regen en sneeuwbuien : Rain and snow shower : Onweer : Thunderstorm : Motregen en regen : Drizzle and rain : Hagel met onweer : Hail with thunderstorm : Nevel : Haze : Lichte motregen en regen : Light drizzle and rain : Lichte regen en korrelhagel/sneeuwpallets met onweer : Light rain and small hail/snow pallets with thunderstorm 113: Helder : Clear 113: Zonnig : Sunny 116: Deels bewolkt : Partly cloudy 119: Bewolkt : Cloudy 122: Geheel bewolkt : Overcast 143: Mist : Mist 176: Mogelijk plaatselijk regen : Patchy rain possible 179: Mogelijk plaatselijk sneeuw : Patchy snow possible 182: Mogelijk plaatselijk hagel : Patchy sleet possible 185: Mogelijk plaatselijk ijzel : Patchy freezing drizzle possible 200: Mogelijk een onweersbui : Thundery outbreaks possible 227: Sneeuwwinden : Blowing snow 230: Sneeuwstorm : Blizzard 248: Mist : Fog 260: Rijp : Freezing fog 263: Plaatselijk motregen : Patchy light drizzle 266: Motregen : Light drizzle 281: Ijzel : Freezing drizzle 284: Zware ijzel : Heavy freezing drizzle 293: Plaatselijk lichte regen : Patchy light rain 296: Lichte regen : Light rain 299: Af en toe regen : Moderate rain at times 302: Regen : Moderate rain 305: Af en toe zware regen : Heavy rain at times 308: Zware regen : Heavy rain 311: Lichte ijzel : Light freezing rain 314: Zware ijzel : Moderate or heavy freezing rain 317: Lichte hagel : Light sleet 320: Zware hagel : Moderate or heavy sleet 323: Plaatselijk lichte sneeuw : Patchy light snow 326: Lichte sneeuw : Light snow 329: Plaatselijke sneeuw : Patchy moderate snow 332: Sneeuw : Moderate snow 335: Plaatselijk zware sneeuw : Patchy heavy snow 338: Zware sneeuw : Heavy snow 350: Hagel : Ice pellets 353: Lichte regenbuien : Light rain shower 356: Regenbuien : Moderate or heavy rain shower 359: Extreem zware regenbuien : Torrential rain shower 362: Lichte hagelbuien : Light sleet showers 365: Hagelbuien : Moderate or heavy sleet showers 368: Lichte sneeuwbuien : Light snow showers 371: Sneeuwbuien : Moderate or heavy snow showers 386: Plaatselijk lichte regen met onweeer : Patchy light rain with thunder 389: Regen met onweer : Moderate or heavy rain with thunder 392: Plaatselijk lichte sneeuw met onweer : Patchy light snow with thunder 395: Sneeuw met onweer : Moderate or heavy snow with thunder ================================================ FILE: share/translations/nn.txt ================================================ 113: Klart : Clear 113: Sol : Sunny 116: Til dels skya : Partly cloudy 119: Skya : Cloudy 122: Overskya : Overcast 143: Dis : Mist 176: Sjansar for skiftande regn : Patchy rain possible 179: Sjansar for skiftande snø : Patchy snow possible 182: Sjansar for skiftande sludd : Patchy sleet possible 185: Sjansar for skiftande, kald duskregn : Patchy freezing drizzle possible 200: Sjansar for tore : Thundery outbreaks possible 227: Snøfokk : Blowing snow 230: Snøstorm : Blizzard 248: Tåke : Fog 260: Kald tåke : Freezing fog 263: Skiftande, lett duskregn : Patchy light drizzle 266: Lett duskregn : Light drizzle 281: Kald duskregn : Freezing drizzle 284: Tung, kald duskregn : Heavy freezing drizzle 293: Skiftande, lett regn : Patchy light rain 296: Lett regn : Light rain 299: Til tider noko regn : Moderate rain at times 302: Noko regn : Moderate rain 305: Til tider tung regn : Heavy rain at times 308: Tung regn : Heavy rain 311: Lett. kald regn : Light freezing rain 314: Moderat eller tung, kald regn : Moderate or heavy freezing rain 317: Lett sludd : Light sleet 320: Moderat eller tung sludd : Moderate or heavy sleet 323: Skiftande, lett snø : Patchy light snow 326: Lett snø : Light snow 329: Skiftande, moderat snø : Patchy moderate snow 332: Noko snø : Moderate snow 335: Skiftande, kraftige snøfall : Patchy heavy snow 338: Kraftige snøfall : Heavy snow 350: Hagl : Ice pellets 353: Lett regnskur : Light rain shower 356: Moderat eller tung regnskur : Moderate or heavy rain shower 359: Styrtregn : Torrential rain shower 362: Lette sluddbyger : Light sleet showers 365: Moderate eller tunge sluddbyger : Moderate or heavy sleet showers 368: Lette snøbyger : Light snow showers 371: Moderate eller tunge snøbyger : Moderate or heavy snow showers 386: Skiftande, lett regn med tore : Patchy light rain with thunder 389: Moderat eller tung regn med tore : Moderate or heavy rain with thunder 392: Skiftande, lett snø med tore : Patchy light snow with thunder 395: Moderat eller tung snø med tore : Moderate or heavy snow with thunder ================================================ FILE: share/translations/oc-help.txt ================================================ Usatge: $ curl wttr.in # emplaçament actual $ curl wttr.in/cdg # metèo a l'aeropòrt de Paris - Charles de Gaulle Types d'emplacements acceptés: /toulouse # nom de la vila /~Eiffel+tower # emplaçament qual que siá /Москва # nom Unicode o emplaçament qual que siá dins quala que siá lenga /muc # còde aeropòrt (3 letras) /@stackoverflow.com # nom de domeni /94107 # còde postal (sonque pels Estats Units) /-78.46,106.79 # coordenadas GPS Emplacements particuliers: /moon # passas de la luna (ajustar ,+US o ,+France per accedir a las vilas del meteis nom) /moon@2016-10-25 # passas de la luna per aquesta data (@2016-10-25) Unitats: ?m # sistèma metric (per defaut pertot levat als Estats Units d d'America) ?u # USCS (per defaut pels Estats-Units d'America) ?M # afichar la velocitat del vent en m/s Opcion d'afichatge: ?0 # uèi solament ?1 # uèi + deman ?2 # uèi + 2 jorns ?n # version corta (sonque pel jorn e la nuèch) ?q # version silenciosa (cap d'entèsta "Previsions metèo per") ?Q # version super-silencieuse (pas d'en-tête "Prévisions météo pour", pas de nom de la ville) ?T # sequéncias d'ecapament pels terminals desactivadas (cap de colors) Opcions PNG: /paris.png # gnèra un fichièr PNG ?p # ajuta un quadre altorn de la sortida ?t # transparéncia 150 (transparence 150) transparency=... # transparéncia de 0 fins a 255 (255 = cap de transparéncia) Combinar las opcions: /Toulouse?0pq /Toulouse?0pq&lang=oc /Toulouse_0pq.png # dins lo mòde PNG las opcions son especificadas après _ /Rome_0pq_lang=it.png # las opcions longas son separadas per de underscores _ Localizacion: $ curl fr.wttr.in/Toulouse $ curl wttr.in/toulouse?lang=oc $ curl -H "Accept-Language: oc" wttr.in/toulouse Lengas suportadas: FULL_TRANSLATION (Support complèt) PARTIAL_TRANSLATION (Support incomplèt) URLs particularas: /:help # mostra aquesta pagina /:bash.function # foncion bash recomandada wttr() /:translation # mostra las informacions sus la traduccion de wttr.in ================================================ FILE: share/translations/oc.txt ================================================ : Pluèja : Rain : Plugeta, Raissas : Light Rain, Rain Shower : Raissas : Rain Shower 113 : Temps clar : Clear 113 : Solelh : Sunny 116 : Nivolós en partida : Partly cloudy 119 : Nivolós : Cloudy 122 : Ennivolat : Overcast 143 : Brumós : Mist 176 : Possiblas raissas esparpalhadas : Patchy rain possible 179 : Possiblas nevadas esparpalhadas : Patchy snow possible 182 : Possiblas nèus fondudas esparpalhadas : Patchy sleet possible 185 : Possiblas plovinas gelibrantas esparpalhadas : Patchy freezing drizzle possible 200 : Auratges possibles : Thundery outbreaks possible 227 : Nèu e vent : Blowing snow 230 : Blisard : Blizzard 248 : Fums : Fog 260 : Nèbla gelibranta : Freezing fog 263 : Plovina leugièra esparpalhada : Patchy light drizzle 266 : Plovina leugièra : Light drizzle 281 : Plovina gelibranta : Freezing drizzle 284 : Fòrta plovina gelibranta : Heavy freezing drizzle 293 : Plugeta fina : Patchy light rain 296 : Plovina : Light rain 299 : Pluèja moderada intermitenta : Moderate rain at times 302 : Pluèja moderada : Moderate rain 305 : Forte pluie intermittente : Heavy rain at times 308 : Raissa : Heavy rain 311 : Pluie verglaçante légère : Light freezing rain 314 : Pluie verglaçante modérée à forte : Moderate or heavy freezing rain 317 : Aiganèu leugièra : Light sleet 320 : Aiganèu moderada o fòrta : Moderate or heavy sleet 323 : Nevada esparpalhada e leugièra : Patchy light snow 326 : Nevada leugièra : Light snow 329 : Nevada esparpalhada parciala : Patchy moderate snow 332 : Nevada moderada : Moderate snow 335 : Nevada fòrta parciala : Patchy heavy snow 338 : Nevada fòrta : Heavy snow 350 : Pèiras de glaça : Ice pellets 353 : Pluèja leugièra : Light rain shower 356 : Pluèja moderada a fòrta : Moderate or heavy rain shower 359 : Pluèja torrenciala : Torrential rain shower 362 : Granissa leugièra : Light sleet showers 365 : Granissa moderada a fòrta : Moderate or heavy sleet showers 368 : Nevadas leugièra : Light snow showers 371 : Granissa moderada a violenta : Moderate or heavy snow showers 386 : Pluèja auratjosa leugièra esparpalhada : Patchy light rain with thunder 389 : Pluèja auratjosa moderada a violenta : Moderate or heavy rain with thunder 392 : Nevada auratjosa esparpalhada e leugièra : Patchy light snow with thunder 395 : Nevada auratjosa moderada a violenta : Moderate or heavy snow with thunder ================================================ FILE: share/translations/pl-help.txt ================================================ Użycie: $ curl wttr.in # aktualna lokalizacja $ curl wttr.in/waw # wybrana lokalizacja (WAW - Lotnisko Okęcie) Opcje wyboru lokalizacji: /paris # miasto /~Eiffel+tower # miejsce /Москва # nazwa miasta w dowolnym języku (jako Unicode string) /waw # kod lotniska IATA (3 znaki) /@stackoverflow.com # domena /94107 # kod obszaru (tylko w USA) /-78.46,106.79 # współrzędne geograficzne Specjalne miejsca: /moon # faza księżyca (używając n.p. ,+US lub ,+France zostanie wyświetlona faza dla podanego miejsca) /moon@2016-10-25 # faza księżyca danego dnia (@2016-10-25) Jednostki: ?m # system metryczny (SI) (standard poza USA) ?u # USCS (standard w USA) ?M # Prędkość wiatru w m/s Ustawienia wyświetlania: ?0 # Pokaż jedynie aktualną pogodę ?1 # Pokaż pogodę na jutro ?2 # Pokaż pogodę na pojutrze ?A # Zignoruj User-Agent i wymuś format wyjścia ANSI (terminal) ?F # nie pokazuj linii "Subskrybuj" ?n # Wersja kompaktowa (tylko noc i dzień) ?q # Wersja okrojona (bez tekstu 'Pogoda w') ?Q # Wersja bardziej okrojona (bez tekstu 'Pogoda w' i nazwy miasta) ?T # Wersja jednokolorowa Opcje PNG: /paris.png # Generuje plik PNG ?p # Dodaje obramowanie do obrazka ?t # Przezroczystość 150 transparency=... # Przezroczystość między 0 a 255 (255 = brak przezroczystości) background=... # Kolor tła w formie RRGGBB Opcje mogą być ze sobą łączone: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # jeśli generujemy obrazek PNG, to te opcje podajemy po _ (znaku podkreślenia) /Rome_0pq_lang=it.png # _ (znakiem podkreślenia) oddzielamy opcje od siebie Lokalizacja: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Obsługiwane języki: FULL_TRANSLATION (obsługiwane) PARTIAL_TRANSLATION (w tłumaczeniu) Strony specjalne: /:help # wyświetla tę stronę /:bash.function # wyświetla proponowaną funkcję `wttr()` dla powłoki bash /:translation # wyświetla informacje o tłumaczach ================================================ FILE: share/translations/pl.txt ================================================ 113: Bezchmurnie : Clear 113: Słonecznie : Sunny 116: Lekkie zachmurzenie : Partly cloudy 119: Zachmurzenie : Cloudy 122: Całkowite zachmurzenie : Overcast 143: Zamglenie : Mist 176: Możliwe miejscowe opady deszczu : Patchy rain possible 179: Możliwe miejscowe opady śniegu : Patchy snow possible 182: Możliwe miejscowe opady deszczu ze śniegiem : Patchy sleet possible 185: Możliwe miejscowe opady marznącego deszczu : Patchy freezing drizzle possible 200: Możliwa burza : Thundery outbreaks possible 227: Zawieja śnieżna : Blowing snow 230: Śnieżyca : Blizzard 248: Mgła : Fog 260: Marznąca mgła : Freezing fog 263: Przelotna mżawka : Patchy light drizzle 266: Mżawka : Light drizzle 281: Marznąca mżawka : Freezing drizzle 284: Marznąca mżawka : Heavy freezing drizzle 293: Przelotne lekkie opady deszczu : Patchy light rain 296: Lekkie opady deszczu : Light rain 299: Przelotne umiarkowane opady deszczu : Moderate rain at times 302: Umiarkowane opady deszczu : Moderate rain 305: Przelotne silne opady deszczu : Heavy rain at times 308: Silne opady deszczu : Heavy rain 311: Lekki marznący deszcz : Light freezing rain 314: Umiarkowany lub silny marznący deszcz : Moderate or heavy freezing rain 317: Lekki deszcz ze śniegiem : Light sleet 320: Umiarkowany lub silny deszcz ze śniegiem : Moderate or heavy sleet 323: Miejscowe lekkie opady śniegu : Patchy light snow 326: Lekkie opady śniegu : Light snow 329: Miejscowe umiarkowane opady śniegu : Patchy moderate snow 332: Umiarkowane opady śniegu : Moderate snow 335: Miejscowe silne opady śniegu : Patchy heavy snow 338: Silne opady śniegu : Heavy snow 350: Gradobicie : Ice pellets 353: Lekkie opady deszczu : Light rain shower 356: Umiarkowane lub silne opady deszczu : Moderate or heavy rain shower 359: Oberwanie chmury : Torrential rain shower 362: Lekki deszcz ze śniegiem : Light sleet showers 365: Umiarkowany lub silny deszcz ze śniegiem : Moderate or heavy sleet showers 368: Lekkie opady śniegu : Light snow showers 371: Umiarkowane lub silne opady śniegu : Moderate or heavy snow showers 386: Miejscowe lekkie opady deszczu i burza z piorunami : Patchy light rain with thunder 389: Umiarkowane lub silne opady deszczu i burza z piorunami : Moderate or heavy rain with thunder 392: Miejscowe lekkie opady śniegu i burza z piorunami : Patchy light snow with thunder 395: Umiarkowane lub silne opady śniegu i burza z piorunami : Moderate or heavy snow with thunder ================================================ FILE: share/translations/pt-br-help.txt ================================================ Uso: $ curl wttr.in # apresenta o clima na sua localização atual $ curl wttr.in/muc # apresenta o clima no aeroporto de Munique Tipos de locais suportados: /paris # o nome de uma cidade /~Eiffel+tower # o nome de um lugar famoso ("+" para espaços) /Москва # o nome Unicode de qualquer lugar em qualquer idioma /muc # o código de um aeroporto (3 letras) /@stackoverflow.com # o nome de um domínio web /94107 # um código de área /-78.46,106.79 # as coordenadas do GPS de um lugar Lugares especiais: /moon # as fases da Lua (adicione "+US" ou "+France" para ver as fases da lua nesse lugares) /moon@2016-10-25 # as fases da Lua em uma determinada data (25/10/2016) Unidades: m # Sistema Internacional de Unidades (SI) (o padrão em todos os lugares exceto nos EUA) u # Sistema Unificado de Clasificaçāo de Solo ou USCS (o padrão nos EUA) M # mostra a velocidade do vento em m/s Opçōes de visualização: 0 # somente o clima atual 1 # o clima atual + a previsão de 1 dia 2 # o clima atual + a previsão de 2 dias A # ignora o User-Agent e força o formato de saída ANSI (terminal) d # restringe a saída aos caracteres da fonte padrão do console F # não imprime a linha "Seguir" n # versão curta (só o dia e a noite) q # versão simples (sem o texto de "Previsão do Tempo") Q # versão super simples (sem "Previsão do Tempo" e sem o nome da cidade) T # imprime uma versão sem cores Opçōes de PNG: /paris.png # gera uma imagem PNG p # acrescenta uma borda ao redor da imagem t # define a transparência em 150 transparency=... # transparência de 0 a 255 (255 = sem transparência) As opções podem ser usadas em conjunto: (observe que para as opções de PNG, o nome do local deve anteceder um "_" e para as demais opções "?") /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # em PNG as opções se especificam depois do caracter _ /Rome_0pq_lang=it.png # uma longa sequência de opções podem ser separadas pelo caracter _ Tradução: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Idiomas suportadas: FULL_TRANSLATION (suportadas) PARTIAL_TRANSLATION (em andamento) URLs especiais: /:help # mostra esta página /:bash.function # sugere uma função wttr() em bash /:translation # mostra informações a respeito dos tradutores ================================================ FILE: share/translations/pt-br.txt ================================================ 113: Limpo : Clear 113: Ensolarado : Sunny 116: Céu com nuvens : Partly cloudy 119: Encoberto : Cloudy 122: Nublado : Overcast 143: Névoa : Mist 176: Chuva fraca próxima : Patchy rain nearby 176: Possível chuva fraca : Patchy rain possible 179: Possível neve fraca : Patchy snow possible 182: Possível granizo : Patchy sleet possible 185: Possível garoa gelada : Patchy freezing drizzle possible 200: Chuvarada próxima : Thundery outbreaks in nearby 200: Possível chuvarada : Thundery outbreaks possible 227: Neve com vento : Blowing snow 230: Nevasca : Blizzard 248: Nevoeiro : Fog 260: Névoa congelante : Freezing fog 263: Garoa irregular : Patchy light drizzle 266: Garoa : Light drizzle 281: Garoa gelada : Freezing drizzle 284: Garoa muito gelada : Heavy freezing drizzle 293: Garoa irregular : Patchy light rain 296: Garoa : Light rain 299: Chuva média ocasional : Moderate rain at times 302: Chuva média : Moderate rain 305: Chuva forte ocasional : Heavy rain at times 308: Chuva forte : Heavy rain 311: Garoa gelada : Light freezing rain 314: Chuva forte/média gelada : Moderate or heavy freezing rain 317: Granizo fraco : Light sleet 320: Granizo forte/médio : Moderate or heavy sleet 323: Neve fraca irregular : Patchy light snow 326: Neve fraca : Light snow 329: Neve média irregular : Patchy moderate snow 332: Neve média : Moderate snow 335: Neve forte irregular : Patchy heavy snow 338: Neve forte : Heavy snow 350: Pelotas de gelo : Ice pellets 353: Chuva fraca : Light rain shower 356: Chuva forte/média : Moderate or heavy rain shower 359: Chuva torrencial : Torrential rain shower 362: Chuva de granizo fraco : Light sleet showers 365: Chuva de granizo forte/média : Moderate or heavy sleet showers 368: Chuva de neve fraca : Light snow showers 371: Chuva de neve forte/média : Moderate or heavy snow showers 386: Garoa irregular com trovão : Patchy light rain with thunder 389: Chuva forte/média com trovão : Moderate or heavy rain in area with thunder 389: Chuva forte/média com trovão : Moderate or heavy rain with thunder 392: Neve fraca com trovão : Patchy light snow with thunder 395: Neve forte/média com trovão próxima : Moderate or heavy snow in area with thunder 395: Neve forte/média com trovão : Moderate or heavy snow with thunder ================================================ FILE: share/translations/pt-help.txt ================================================ Utilização: $ curl wttr.in # o clima na sua localização atual $ curl wttr.in/muc # o clima no aeroporto de Munique Tipos de localização suportados: /paris # o nome de uma cidade /~Eiffel+tower # o nome de um lugar famoso (+ para espaços) /Москва # o nome Unicode de qualquer lugar em qualquer idioma /muc # o código de um aeroporto (3 letras) /@stackoverflow.com # o nome de um domínio web /94107 # um código de área /-78.46,106.79 # coordenadas do GPS Informações sobre as fases da lua: /moon # A fase da lua (acrescente ,+US ou ,+France para estas cidades) /moon@2016-10-25 # A fase da lua numa determinada data (@2016-10-25) Unidades: m # sistema métrico (SI) (usado por omissão em todos os lugares exceto nos EUA) u # sistema Unificado de Clasificaçāo de Solo ou USCS (usado por omissão nos EUA) M # mostrar a velocidade do vento em m/s Opçōes de visualização: 0 # apenas o clima atual 1 # o clima atual + a previsão de hoje 2 # o clima atual + as previsões de hoje + amanhã A # ignora User-Agent e força output ANSI (terminal) d # restringe o output a glifos da fonte padrão da consola F # não mostra a linha "Segue" n # versão curta (só o dia e a noite) q # versão silenciosa (sem o texto de "Previsão do Tempo") Q # versão super-silenciosa (sem "Previsão do Tempo" e o nome da cidade) T # desliga as sequências de terminal (sem cores) Opçōes de PNG: /paris.png # gera uma imagem PNG p # acrescenta uma borda ao redor da imagem t # transparência 150 transparency=... # transparência de 0 a 255 (255 = sem transparência) background=... # cor de fundo na forma RRGGBB, por exemplo, 00aaaa As opções podem ser usadas em conjunto: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # em PNG as opções especificam-se depois do caracter _ /Rome_0pq_lang=it.png # opções longas podem ser separadas com um caracter _ Localizaçāo: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Idiomas suportados: FULL_TRANSLATION (suportadas) PARTIAL_TRANSLATION (em curso) URLs especiais: /:help # mostra esta página /:bash.function # mostra uma função bash wttr() recomendada /:translation # mostra informação a respeito dos tradutores ================================================ FILE: share/translations/pt.txt ================================================ 113: Limpo : Clear 113: Ensolarado : Sunny 116: Céu com nuvens : Partly cloudy 119: Encoberto : Cloudy 122: Nublado : Overcast 143: Névoa : Mist 176: Chuva fraca nas proximidades : Patchy rain nearby 176: Possibilidade de chuva irregular : Patchy rain possible 179: Possibilidade de neve irregular : Patchy snow possible 182: Possibilidade de granizo : Patchy sleet possible 185: Possibilidade de chuvisco gelado : Patchy freezing drizzle possible 200: Trovoada nas proximidades : Thundery outbreaks in nearby 200: Possibilidade de trovoada : Thundery outbreaks possible 227: Neve com vento : Blowing snow 230: Nevasca : Blizzard 248: Nevoeiro : Fog 260: Nevoeiro gelado : Freezing fog 263: Chuvisco irregular : Patchy light drizzle 266: Chuviscos : Light drizzle 281: Chuvisco gelado : Freezing drizzle 284: Chuvisco muito gelado : Heavy freezing drizzle 293: Chuvisco irregular : Patchy light rain 296: Chuva fraca : Light rain 299: Chuva moderada ocasionalmente : Moderate rain at times 302: Chuva moderada : Moderate rain 305: Chuva forte ocasional : Heavy rain at times 308: Chuva forte : Heavy rain 311: Chuvisco gelado : Light freezing rain 314: Chuva moderada ou forte gelada : Moderate or heavy freezing rain 317: Granizo fraco : Light sleet 320: Granizo moderado ou forte : Moderate or heavy sleet 323: Neve fraca irregular : Patchy light snow 326: Neve fraca : Light snow 329: Neve moderada irregular : Patchy moderate snow 332: Neve moderada : Moderate snow 335: Neve forte irregular : Patchy heavy snow 338: Neve forte : Heavy snow 350: Pelotas de gelo : Ice pellets 353: Chuva fraca : Light rain shower 356: Chuva moderada ou forte : Moderate or heavy rain shower 359: Chuva torrencial : Torrential rain shower 362: Chuva de granizo fraco : Light sleet showers 365: Chuvas moderadas ou fortes de granizo : Moderate or heavy sleet showers 368: Chuva de neve fraca : Light snow showers 371: Chuvas moderadas ou fortes de neve : Moderate or heavy snow showers 386: Chuva fraca e irregular com trovoada : Patchy light rain with thunder 389: Chuva moderada ou forte na área com trovoada : Moderate or heavy rain in area with thunder 389: Chuva moderada ou forte com trovoada : Moderate or heavy rain with thunder 392: Neve fraca com trovoada : Patchy light snow with thunder 395: Neve moderada ou forte na área com trovoada : Moderate or heavy snow in area with thunder 395: Neve moderada ou forte com trovoada : Moderate or heavy snow with thunder ================================================ FILE: share/translations/ro-help.txt ================================================ Utilizare: $ curl wttr.in # localizare curentă $ curl wttr.in/otp # Aeroportul Internațional Henri Coandă (OTP) Tipuri de localizări acceptate: /Cluj # numele localității /~coloana+infinitului # locuri binecunoscute sau puncte de interes /Москва # nume Unicode în orice limbă /otp # cod aeroport (3 litere) /@stackoverflow.com # nume de domeniu /94107 # cod poștal (doar pentru Statele Unite) /-78.46,106.79 # coordonate GPS Localizări speciale: /moon # faza lunii (adăugați ,+US sau ,+France pentru a arăta orașele cu acest nume) /moon@2016-10-25 # faza lunii într-o anumită dată (@2016-10-25) Unități de măsură: ?m # sistem metric (implicit, mai puțin pentru Statele Unite ale Americii) ?u # USCS (implicit pentru Statele Unite ale Americii) ?M # afișează viteza vântului în m/s Opțiuni de afișare: ?0 # azi ?1 # azi și mâine ?2 # azi, mâine și poimâine ?n # doar ziua și noaptea ?q # fără antet "Prognoza meteo pentru" ?Q # fără antet "Prognoza meteo pentru", fără numele localității ?T # fără coduri speciale în terminal (fără culori) Opțiuni PNG: /Băicoi.png # generează un fișier PNG ?p # cu chenar ?t # cu transparență 150 transparency=... # cu transparență în intervalul [0-255] (255 = fără transparență) Combinarea opțiunilor: /Lehliu-Gară?0q /Vaslui?0q&lang=ro /Videle_0pq.png # în modul PNG opțiunile se specifică după _ /Roma_0pq_lang=it.png # opțiunile cu nume lung se separă prin _ Limba de afișare: $ curl ro.wttr.in/Tărtășești $ curl wttr.in/Paris?lang=fr $ curl -H "Accept-Language: de" wttr.in/berlin Limbi suportate: FULL_TRANSLATION (Suport complet) PARTIAL_TRANSLATION (Suport incomplet) URL-uri speciale: /:help # afișează această pagină /:bash.function # funcție bash wttr() /:translation # afișează informații despre traducerile din wttr.in ================================================ FILE: share/translations/ro.txt ================================================ 113: Senin : Clear 113: Însorit : Sunny 116: Parțial înnorat : Partly cloudy 119: Înnorat : Cloudy 122: Cer acoperit : Overcast 143: Ceață : Mist 176: Posibilă ploaie pe alocuri : Patchy rain possible 179: Posibilă ninsoare pe alocuri : Patchy snow possible 182: Posibilă lapoviță pe alocuri : Patchy sleet possible 185: Posibilă burniță înghețată pe alocuri : Patchy freezing drizzle possible 200: Posibile tunete : Thundery outbreaks possible 227: Zăpadă în rafale : Blowing snow 230: Viscol : Blizzard 248: Ceață : Fog 260: Ceață înghețată : Freezing fog 263: Burniță ușoară pe alocuri : Patchy light drizzle 266: Burniță ușoară : Light drizzle 281: Burniță înghețată : Freezing drizzle 284: Burnită înghețată abundentă : Heavy freezing drizzle 293: Ploaie ușoară pe alocuri : Patchy light rain 296: Ploaie ușoară : Light rain 299: Momente de ploaie moderată : Moderate rain at times 302: Ploaie moderată : Moderate rain 305: Momente de ploaie torențială : Heavy rain at times 308: Ploaie torențială : Heavy rain 311: Ploaie înghețată ușoară : Light freezing rain 314: Ploaie înghețată moderată sau torențială : Moderate or heavy freezing rain 317: Lapoviță ușoară : Light sleet 320: Lapoviță moderată sau torențială : Moderate or heavy sleet 323: Ninsoare ușoară pe alocuri : Patchy light snow 326: Ninsoare ușoară : Light snow 329: Ninsoare moderată pe alocuri : Patchy moderate snow 332: Ninsoare moderată : Moderate snow 335: Ninsoare abundentă pe alocuri : Patchy heavy snow 338: Ninsoare abundentă : Heavy snow 350: Grindină : Ice pellets 353: Averse de ploaie ușoară : Light rain shower 356: Averse de ploaie moderată sau torențială : Moderate or heavy rain shower 359: Averse de ploaie torențială : Torrential rain shower 362: Averse de lapoviță ușoară : Light sleet showers 365: Averse de lapoviță moderată sau torențială : Moderate or heavy sleet showers 368: Averse de ninsoare ușoară : Light snow showers 371: Averse de ninsoare moderată sau torențială : Moderate or heavy snow showers 386: Ploi ușoare cu tunete pe alocuri : Patchy light rain with thunder 389: Ploaie moderată sau furtună cu tunete : Moderate or heavy rain with thunder 392: Ninsoare ușoară cu tunete pe alocuri : Patchy light snow with thunder 395: Ninsoare moderată sau abundentă cu tunete : Moderate or heavy snow with thunder ================================================ FILE: share/translations/ru-help.txt ================================================ Использование: $ curl wttr.in # текущее местоположение $ curl wttr.in/svo # погода в аэропорту Шереметьево (код ICAO: SVO) Поддерживаемые типы местоположений: /paris # город /~Eiffel+tower # любое местоположение /Москва # юникодное имя любого местоположения на любом языке /muc # код аэропорта ICAO (3 буквы) /@stackoverflow.com # доменное имя /94107 # почтовый индекс (только для США) /-78.46,106.79 # GPS-координаты Специальные условные местоположения: /moon # Фаза Луны (добавьте ,+US или ,+France для города Moon в США/Франции) /moon@2016-10-25 # Фаза Луны для указанной даты (@2016-10-25) Единицы измерений: ?m # метрические (СИ) (используются везде кроме США) ?u # USCS (используются в США) ?M # показывать скорость ветра в м/с Опции отображения: ?0 # только текущая погода ?1 # погода сегодня + 1 день ?2 # погода сегодня + 2 дня ?n # узкая версия (только день и ночь) ?q # тихая версия (без текста "Прогноз погоды") ?Q # сверхтихая версия (без "Прогноз погоды", нет названия города) ?T # отключить терминальные последовательности (без цветов) PNG-опции: /paris.png # сгенерировать PNG-файл ?p # добавить рамочку вокруг ?t # transparency=150 (прозрачность 150) transparency=... # прозрачность от 0 до 255 (255 = не прозрачный) Опции можно комбинировать: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # в PNG-запросах опции указываются после _ /Rome_0pq_lang=it.png # длинные опции разделяются знаком подчёркивания _ Локализация: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Поддерживаемые языки: FULL_TRANSLATION (поддерживаются) PARTIAL_TRANSLATION (в процессе) Специальные страницы: /:help # показать эту страницу /:bash.function # показать рекомендованную функцию wttr() /:translation # показать список переводчиков wttr.in ================================================ FILE: share/translations/ru.txt ================================================ : Мгла в окрестностях : Haze in vicinity : : Сильный ливневой дождь со снегом : Heavy rain and snow shower : : Сильный ливневой дождь : Heavy rain shower : : Слабая морось и снежные зёрна : Light drizzle and snow grains : : Слабый ливневой дождь с дымкой : Light rain and mist shower : : Слабый ливень из мелкого града/снежной крупы : Light small hail/snow pellets shower : : Слабые ливневые осадки неустановленного вида : Light unknown precipitation shower : : Гроза, дождь с мелким градом/снежной крупой : Rain and small hail/snow pellets with thunderstorm : : Ливневой снег в окрестностях : Snow shower in vicinity : : Грозовая активность неподалёку : Thundery outbreaks nearby : : Позёмка : Low drifting snow : : Туман на ряде участков : Patches of fog : : Морось : Drizzle : : Слабая морось и дождь : Light drizzle and rain : : Снег : Snow : : Дождь : Rain : : Ливневый дождь : Rain Shower : : Песчаная низовая метель : Blowing sand : : Повсеместная пылевая низовая метель : Blowing widespread dust : : Повсеместная пылевая низовая метель в окрестностях : Blowing widespread dust in vicinity : : Морось и туман : Drizzle and fog : : Морось и дождь : Drizzle and rain : : Ливневый дождь и морось : Drizzle and rain shower : : Гроза с моросью : Drizzle with thunderstorm : : Пыльная буря : Dust storm : : Туман в окрестностях : Fog in vicinity : : Переохлаждённая морось и дождь : Freezing drizzle and rain : : Переохлаждённая дымка : Freezing mist : : Переохлаждённый дождь : Freezing rain : : Переохлаждённый дождь и снег : Freezing rain and snow : : Переохлаждённые осадки неустановленного вида : Freezing unknown precipitation : : Воронкообразное облако в окрестностях : Funnel cloud in vicinity : : Ливень с градом : Hail shower : : Гроза с градом : Hail with thunderstorm : : Мгла : Haze : : Сильная низовая метель : Heavy blowing snow : : Сильная морось : Heavy drizzle : : Сильная морось и дождь : Heavy drizzle and rain : : Сильные переохлаждённые осадки неустановленного вида : Heavy freezing unknown precipitation : : Гроза с сильным градом : Heavy hail with thunderstorm : : Сильная ледяная крупа : Heavy ice pallets : : Сильный ливневой дождь с градом : Heavy rain and hail shower : : Гроза с сильным дождём и градом : Heavy rain and hail with thunderstorm : : Сильный ливневой дождь с мелким градом/снежной крупой : Heavy rain and small hail/snow pallets shower : : Гроза, сильный дождь с мелким градом/снежной крупой : Heavy rain and small hail/snow pallets with thunderstorm : : Сильный дождь со снегом : Heavy rain and snow : : Гроза, сильный дождь со снегом/мелким градом/снежной крупой : Heavy rain and snow and small hail/snow pallets with thunderstorm : : Гроза с сильным дождём : Heavy rain with thunderstorm : : Сильный ливень из мелкого града/снежной крупы : Heavy small hail/snow pallets shower : : Гроза с сильным мелким градом/снежной крупой : Heavy small hail/snow pallets with thunderstorm : : Сильный ливень из снега с мелким градом/снежной крупой : Heavy snow and small hail/snow pallets shower : : Гроза, сильный снег с мелким градом/снежной крупой : Heavy snow and small hail/snow pallets with thunderstorm : : Сильный ливень из снега : Heavy snow shower : : Гроза, сильный снег : Heavy snow with thunderstorm : : Осадки в виде кристаллов льда : Ice crystals : : Осадки в виде ледяной крупы : Ice pallets : : Слабая низовая метель : Light blowing snow : : Слабая морось и туман : Light drizzle and fog : : Слабая морось и ледяная крупа : Light drizzle and ice pallets : : Слабая морось, дождь и снег : Light drizzle and rain and snow : : Ливневый дождь и слабая морось : Light drizzle and rain shower : : Гроза, слабая морось и дождь : Light drizzle and rain with thunderstorm : : Слабая морось, снег : Light drizzle and snow : : Гроза, слабая морось : Light drizzle with thunderstorm : : Слабая переохлаждённая морось : Light freezing drizzle : : Слабая переохлаждённая морось и дождь : Light freezing drizzle and rain : : Слабая переохлаждённая морось и снег : Light freezing drizzle and snow : : Слабый переохлаждённый туман : Light freezing fog : : Слабый переохлаждённый дождь и снег : Light freezing rain and snow : : Слабый переохлаждённый туман и снежные зёрна : Light freezing snow grains and fog : : Слабые переохлаждённые осадки неустановленного вида : Light freezing unknown precipitation : : Слабый ливень из града : Light hail shower : : Слабое выпадение ледяных кристаллов : Light ice crystals : : Слабое выпадение ледяной крупы : Light ice pallets : : Слабый позёмок : Light low drifting : : Слабый позёмный снегоперенос : Light low drifting snow : : Слабый ливневой дождь с градом : Light rain and hail shower : : Гроза, слабый дождь с градом : Light rain and hail with thunderstorm : : Слабый дождь с ледяной крупой : Light rain and ice pallets : : Слабый дождь и дымка : Light rain and mist : : Слабый ливневой дождь с мелким градом/снежной крупой : Light rain and small hail/snow pallets shower : : Гроза, слабый дождь и мелкий град/снежная крупа : Light rain and small hail/snow pallets with thunderstorm : : Слабый дождь и снег : Light rain and snow : : Слабый ливневой дождь с мелким градом/снежной крупой и снегом : Light rain and snow and small hail/snow pallets shower : : Слабый дождь и снежные зёрна : Light rain and snow grains : : Слабый ливневой дождь со снегом : Light rain and snow shower : : Гроза, слабый дождь и снег : Light rain and snow with thunderstorm : : Гроза, слабый дождь : Light rain with thunderstorm : : Слабый ливень : Light shower : : Слабые ливни из ледяной крупы : Light showers of ice pellets : : Слабые осадки из мелкого града/снежной крупы : Light small hail/snow pallets : : Слабые ливневые осадки из мелкого града/снежной крупы : Light small hail/snow pallets shower : : Гроза, слабые осадки из мелкого града/снежной крупы : Light small hail/snow pallets with thunderstorm : : Слабый снег с ледяной крупой : Light snow and ice pallets : : Слабый снег с мелким градом/снежной крупой : Light snow and small hail/snow pallets : : Слабый ливень из снега и мелкого града/снежной крупы : Light snow and small hail/snow pallets shower : : Гроза, снег с мелким градом/снежной крупой : Light snow and small hail/snow pallets with thunderstorm : : Слабые осадки в виде снега и снежных зёрен : Light snow and snow grains : : Слабые осадки в виде снежных зёрен : Light snow grains : : Слабый ливневый снег : Light snow shower : : Гроза, слабые осадки в виде снега : Light snow with thunderstorm : : Слабая гроза : Light thunderstorm : : Слабые осадки неустановленного вида : Light unknown precipitation : : Слабые сформированные пылевые/песчаные вихри : Light well-developed dust/sand whirls : : Песчаный позёмок : Low drifting sand : : Повсеместный пылевой позёмок : Low drifting widespread dust : : Гроза с дымкой : Mist with thunderstorm : : Грозовая активность, сильный или умеренный дождь в окрестностях : Moderate or heavy rain in area with thunder : : Грозовая активность, сильный или умеренный снег в окрестностях : Moderate or heavy snow in area with thunder : : Местами туман : Partial fog : : Туман на ряде участков в окрестностях : Patches of fog in vicinity : : Переохлаждённая морось на ряде участков неподалёку : Patchy freezing drizzle nearby : : Грозовая активность, на ряде участков слабый дождь в окрестностях : Patchy light rain in area with thunder : : Грозовая активность, на ряде участков слабый снег в окрестностях : Patchy light snow in area with thunder : : Дождь на ряде участков неподалёку : Patchy rain nearby : : Мокрый снег на ряде участков неподалёку : Patchy sleet nearby : : Снег на ряде участков неподалёку : Patchy snow nearby : : Ливневый дождь с градом : Rain and hail shower : : Гроза, дождь с градом : Rain and hail with thunderstorm : : Дождь и дымка : Rain and mist : : Гроза, дождь и дымка : Rain and mist with thunderstorm : : Ливневый дождь с мелким градом/снежной крупой : Rain and small hail/snow pallets shower : : Гроза, дождь с мелким градом/снежной крупой : Rain and small hail/snow pallets with thunderstorm : : Дождь и снег : Rain and snow : : Ливневый дождь с мелким градом/снежной крупой и снегом : Rain and snow and small hail/snow pallets shower : : Дождь со снежными зёрнами : Rain and snow grains : : Ливневый дождь и снег : Rain and snow shower : : Гроза, дождь и снег : Rain and snow with thunderstorm : : Дождь в окрестностях : Rain in vicinity : : Ливневый дождь в окрестностях : Rain shower in vicinity : : Гроза с дождём : Rain with thunderstorm : : Осадки в виде частиц песка : Sand : : Песчаная буря : Sandstorm : : Приземный туман : Shallow fog : : Приземная дымка : Shallow mist : : Ливень : Shower : : Ливень в окрестностях : Shower in vicinity : : Осадки в виде мелкого града/снежной крупы : Small hail/snow pallets : : Ливневые осадки в виде мелкого града/снежной крупы : Small hail/snow pallets shower : : Гроза, осадки в виде мелкого града/снежной крупы : Small hail/snow pallets with thunderstorm : : Дым : Smoke : : Осадки в виде снега и ледяной крупы : Snow and ice pallets : : Снег и дымка : Snow and mist : : Ливневые осадки в виде снега и мелкого града/снежной крупы : Snow and small hail/snow pellets shower : : Гроза, осадки в виде снега и мелкого града/снежной крупы : Snow and small hail/snow pallets with thunderstorm : : Снег и снежные зёрна : Snow and snow grains : : Снежные зёрна : Snow grains : : Ливневые осадки в виде снега : Snow shower : : Гроза, осадки в виде снега : Snow with thunderstorm : : Шквал : Squalls : : Гроза : Thunderstorm : : Гроза в окрестностях : Thunderstorm in vicinity : : Грозовая активность неподалёку : Thundery outbreaks in nearby : : Осадки неустановленного вида : Unknown precipitation : : Ливневые осадки неустановленного вида : Unknown precipitation shower : : Гроза с осадками неустановленного вида : Unknown precipitation with thunderstorm : : Вулканическая пыль : Volcanic ash : : Вулканическая пыль в окрестностях : Volcanic ash in vicinity : : Сформированные пылевые/песчаные вихри в окрестностях : Well-developed dust/sand whirls in vicinity : : Повсеместно пыль в воздухе : Widespread dust : 113: Ясно : Clear : 113: Солнечно : Sunny : 116: Переменная облачность : Partly cloudy : 119: Облачно : Cloudy : 122: Пасмурно : Overcast : 143: Дымка : Mist : 176: На ряде участков возможен дождь : Patchy rain possible : 179: На ряде участков возможен снег : Patchy snow possible : 182: На ряде участков возможен мокрый снег : Patchy sleet possible : 185: На ряде участков возможна переохлаждённая морось : Patchy freezing drizzle possible : 200: Возможна грозовая активность : Thundery outbreaks possible : 227: Снежная низовая метель : Blowing snow : 230: Близзард : Blizzard : 248: Туман : Fog : 260: Переохлаждённый туман : Freezing fog : 263: На ряде участков слабая морось : Patchy light drizzle : 266: Слабая морось : Light drizzle : 281: Переохлаждённая морось : Freezing drizzle : 284: Сильные осадки в виде переохлаждённой мороси : Heavy freezing drizzle : 293: На ряде участков слабый дождь : Patchy light rain : 296: Слабый дождь : Light rain : 299: Временами умеренный дождь : Moderate rain at times : 302: Умеренный дождь : Moderate rain : 305: Временами сильный дождь : Heavy rain at times : 308: Сильный дождь : Heavy rain : 311: Слабый переохлаждённый дождь : Light freezing rain : 314: Умеренный или сильный переохлаждённый дождь : Moderate or heavy freezing rain : 317: Слабый мокрый снег : Light sleet : 320: Умеренный или сильный мокрый снег : Moderate or heavy sleet : 323: На ряде участков слабый снег : Patchy light snow : 326: Слабые осадки в виде снега : Light snow : 329: На ряде участков умеренный снегопад : Patchy moderate snow : 332: Умеренный снегопад : Moderate snow : 335: На ряде участков сильный снегопад : Patchy heavy snow : 338: Сильный снегопад : Heavy snow : 350: Осадки в виде ледяной крупы : Ice pellets : 353: Слабый ливневой дождь : Light rain shower : 356: Сильный или умеренный ливневой дождь : Moderate or heavy rain shower : 359: Проливной ливневый дождь : Torrential rain shower : 362: Слабый ливневой мокрый снег : Light sleet showers : 365: Сильный или умеренный ливневой мокрый снег : Moderate or heavy sleet showers : 368: Слабые ливневые осадки в виде снега : Light snow showers : 371: Сильные или умеренные ливнёвые осадки в виде снега : Moderate or heavy snow showers : 386: Грозовая активность, на ряде участков слабый дождь : Patchy light rain with thunder : 389: Грозовая активность, сильный или умеренный дождь : Moderate or heavy rain with thunder : 392: Грозовая активность, на ряде участков слабые осадки в виде снега : Patchy light snow with thunder : 395: Грозовая активность, сильные или умеренные осадки в виде снега : Moderate or heavy snow with thunder : ================================================ FILE: share/translations/sl.txt ================================================ 113: Jasno : Clear 113: Sončno : Sunny 116: Delno oblačno : Partly cloudy 119: Oblačno : Cloudy 122: Pretežno oblačno : Overcast 143: Megleno : Mist 176: Možne plohe : Patchy rain possible 179: Možno občasno sneženje : Patchy snow possible 182: Možen žled : Patchy sleet possible 185: Možna pomrznjena rosa : Patchy freezing drizzle possible 200: Možno neurje z grmenjem : Thundery outbreaks possible 227: Snežno neurje : Blowing snow 230: Snežno neurje : Blizzard 248: Megla : Fog 260: Zmrznjena megla : Freezing fog 263: Rahlo rosenje : Patchy light drizzle 266: Rahel dež : Light drizzle 281: Zmrznjeno rosenje : Freezing drizzle 284: Močno zmrznjeno rosenje : Heavy freezing drizzle 293: Občasno rahel dež : Patchy light rain 296: Rahel dež : Light rain 299: Občasno droben dež : Moderate rain at times 302: Droben dež : Moderate rain 305: Občasno močan dež : Heavy rain at times 308: Močan dež : Heavy rain 311: Rahel zmrznjen dež : Light freezing rain 314: Zmrznjen dež : Moderate or heavy freezing rain 317: Rahel žled : Light sleet 320: Žled : Moderate or heavy sleet 323: Občasno rahlo sneženje : Patchy light snow 326: Rahel sneg : Light snow 329: Občasno rahlo sneženje : Patchy moderate snow 332: Rahel sneg : Moderate snow 335: Občasno močan sneg : Patchy heavy snow 338: Močan sneg : Heavy snow 350: Led : Ice pellets 353: Rahla ploha : Light rain shower 356: Rahle ali močne plohe : Moderate or heavy rain shower 359: Hudourniški dež : Torrential rain shower 362: Rahel žled : Light sleet showers 365: Žled : Moderate or heavy sleet showers 368: Občasno rahlo sneženje : Light snow showers 371: Rahle ali močne snežne padavine : Moderate or heavy snow showers 386: Občasno rahel dež z grmenjem : Patchy light rain with thunder 389: Dež z grmenjem : Moderate or heavy rain with thunder 392: Občasen sneg z grmenjem : Patchy light snow with thunder 395: Sneg z grmenjem : Moderate or heavy snow with thunder ================================================ FILE: share/translations/ta-help.txt ================================================ பயன்பாடு: $ curl wttr.in # தற்போதைய இடம் $ curl wttr.in/cdg # பாரிஸ் - சார்லஸ் டி கோல் விமான நிலையத்தில் வானிலை முன்னறிவிப்பு ஏற்றுக்கொள்ளப்பட்ட வகைகள்: /paris # நகரத்தின் பெயர் /~Eiffel+tower # எந்த இடம் /Москва # யூனிகோட் பெயர் அல்லது எந்த மொழியிலும் எந்த இடம் /muc # விமான நிலைய குறியீடு (3 எழுத்துகள்) /@stackoverflow.com # டொமைன் பெயர் /94107 # அஞ்சல் குறியீடு (அமெரிக்காவில் மட்டும்) /-78.46,106.79 # ஜிபிஎஸ் ஒருங்கிணைப்புகள் சிறப்பு வகைகள்: /moon # சந்திரனின் கட்டங்கள் (அதே பெயரில் உள்ள நகரங்களை அணுக, + அமெரிக்கா அல்லது + பிரான்ஸ் சேர்க்கவும்) /moon@2016-10-25 # இந்த தேதிக்கான சந்திரனின் கட்டங்கள் (@2016-10-25) அலகுகள்: ?m # மெட்ரிக் அமைப்பு (அமெரிக்காவைத் தவிர எல்லா இடங்களிலும் இயல்புநிலை) ?u # USCS (அமெரிக்க ஐக்கிய நாடுகளுக்கு இயல்புநிலை) ?M # காற்றின் வேகத்தை m/s இல் காட்டுகிறது காட்சி விருப்பம்: ?0 # இன்று மட்டும் ?1 # இன்று + நாளை ?2 # இன்று + 2 நாட்கள் ?n # குறுகிய பதிப்பு (பகல் மற்றும் இரவு மட்டும்) ?q # சைலண்ட் பதிப்பு ("தலைப்புக்கான வானிலை முன்னறிவிப்பு" இல்லை) ?Q # சூப்பர்-சைலண்ட் பதிப்பு ("வானிலை முன்னறிவிப்பு" தலைப்பு இல்லை, நகரத்தின் பெயர் இல்லை) ?T # முடக்கப்பட்ட டெர்மினல்களுக்கான தப்பிக்கும் காட்சிகள் (வண்ணங்கள் இல்லை) PNG விருப்பங்கள்: /paris.png # ஒரு PNG கோப்பை உருவாக்கவும் ?p # வெளியீட்டைச் சுற்றி ஒரு சட்டத்தைச் சேர்க்கவும் ?t # வெளிப்படைத்தன்மை 150 transparency=... # 0 முதல் 255 வரை வெளிப்படைத்தன்மை (255 = வெளிப்படைத்தன்மை இல்லை) விருப்பங்களை இணைக்கவும்: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNG பயன்முறையில் விருப்பங்கள் _ க்குப் பிறகு குறிப்பிடப்படுகின்றன /Rome_0pq_lang=it.png # நீண்ட விருப்பங்கள் அடிக்கோடிட்டால் பிரிக்கப்படுகின்றன _ இடம்: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris ஆதரிக்கப்படும் மொழிகள்: FULL_TRANSLATION (முழு மொழிபெயர்ப்பு) PARTIAL_TRANSLATION (முழுமையற்ற மொழிபெயர்ப்பு) URLs குறிப்பாக: /:help # இந்தப் பக்கத்தைக் காட்டவும் /:bash.function # பரிந்துரைக்கப்பட்ட bash செயல்பாடு wttr() /:translation # மொழிபெயர்ப்பு பற்றிய தகவலைக் காட்டுகிறது wttr.in ================================================ FILE: share/translations/ta.txt ================================================ : இடியுடன் கூடிய கனமழை மற்றும் ஆலங்கட்டி மழை : Heavy rain and hail with thunderstorm : இடியுடன் கூடிய கனமழை : Heavy rain with thunderstorm : லேசான மழை மற்றும் இடியுடன் கூடிய ஆலங்கட்டி மழை : Light rain and hail with thunderstorm : லேசான மழை மற்றும் பனி மழை : Light rain and snow shower : லேசான மழை மற்றும் இடியுடன் கூடிய மழை : Light rain with thunderstorm : லேசான பனி மழை : Light snow shower : பகுதி மூடுபனி : Partial fog : இடியுடன் கூடிய ஆலங்கட்டி மழை : Rain and hail with thunderstorm : இடியுடன் கூடிய மழை : Rain with thunderstorm : லேசான மூடுபனி : Shallow fog : புகை : Smoke : பலமான காற்று : Squalls : இடியுடன் கூடிய மழை அருகில் : Thunderstorm in vicinity : பனி : Snow : மழை : Rain : லேசான மழை, மழை பொழிவு : Light Rain, Rain Shower : மழை பொழிவு : Rain Shower : மூடுபனி திட்டுகள் : Patches of fog : தூறல் : Drizzle : லேசான தூறல் : Light drizzle : குறைந்த பனிப்பொழிவு : Low drifting snow : லேசான மழை மற்றும் பனி : Light rain and snow : அருகாமையில் மழை : Shower in vicinity : இடியுடன் கூடிய மழை : Rain with thunderstorm : மழை மற்றும் பனி மழை : Rain and snow shower : இடியுடன் கூடிய மழை : Thunderstorm : தூறல் மற்றும் மழை : Drizzle and rain : இடியுடன் கூடிய ஆலங்கட்டி மழை : Hail with thunderstorm : மூட்டம் : Haze : லேசான தூறல் மற்றும் மழை : Light drizzle and rain : லேசான தூறல் மற்றும் இடியுடன் கூடிய சிறிய ஆலங்கட்டி மழை/பனிப் பலகைகள் : Light rain and small hail/snow pallets with thunderstorm 113 : தெளிந்த வானம் : Clear 113 : வெயில் : Sunny 116 : ஒரளவு மேகமூட்டம் : Partly cloudy 119 : மேகமூட்டம் : Cloudy 122 : முற்றிலும் மேகமூட்டம் : Overcast 143 : பனி மூட்டம் : Mist 176 : சீரற்ற மழை சாத்தியம் : Patchy rain possible 179 : சீரற்ற பனி சாத்தியம் : Patchy snow possible 182 : பனிப்பொழிவு சாத்தியம் : Patchy sleet possible 185 : உறைபனி தூறல் சாத்தியம் : Patchy freezing drizzle possible 200 : இடியுடன் கூடிய மழை சாத்தியமாகும் : Thundery outbreaks possible 227 : வீசும் பனி : Blowing snow 230 : பனிப்புயல் : Blizzard 248 : மூடுபனி : Fog 260 : உறைபனி மூடுபனி : Freezing fog 263 : மெல்லிய தூறல் : Patchy light drizzle 266 : லேசான தூறல் : Light drizzle 281 : உறையும் தூறல் : Freezing drizzle 284 : கடும் உறைபனி தூறல் : Heavy freezing drizzle 293 : சீரற்ற லேசான மழை : Patchy light rain 296 : லேசான மழை : Light rain 299 : அவ்வப்போது மிதமான மழை பெய்யும் : Moderate rain at times 302 : மிதமான மழை : Moderate rain 305 : அவ்வப்போது பலத்த மழை : Heavy rain at times 308 : பலத்த மழை : Heavy rain 311 : லேசான உறைபனி மழை : Light freezing rain 314 : மிதமான அல்லது கடுமையான உறைபனி மழை : Moderate or heavy freezing rain 317 : லேசான தூறல் : Light sleet 320 : மிதமான அல்லது கடுமையான தூறல் : Moderate or heavy sleet 323 : சீரற்ற லேசான பனி : Patchy light snow 326 : லேசான பனி : Light snow 329 : சீரற்ற மிதமான பனி : Patchy moderate snow 332 : மிதமான பனி : Moderate snow 335 : சீரற்ற கடுமையான பனி : Patchy heavy snow 338 : கடுமையான பனி : Heavy snow 350 : பனி துகள்கள் : Ice pellets 353 : லேசான சாரல் மழை : Light rain shower 356 : மிதமான அல்லது கடுமையான சாரல் மழை : Moderate or heavy rain shower 359 : சாரல் மழை : Torrential rain shower 362 : லேசான தூறல் மழை : Light sleet showers 365 : மிதமான அல்லது கனத்த தூறல் மழை பெய்யும் : Moderate or heavy sleet showers 368 : லேசான பனி மழை : Light snow showers 371 : மிதமான அல்லது கனத்த பனி மழை : Moderate or heavy snow showers 386 : சீரற்ற இடியுடன் கூடிய லேசான மழை : Patchy light rain with thunder 389 : இடியுடன் கூடிய மிதமான அல்லது பலத்த மழை : Moderate or heavy rain with thunder 392 : சீரற்ற இடியுடன் கூடிய லேசான பனி : Patchy light snow with thunder 395 : மிதமான அல்லது கனத்த இடியுடன் கூடிய பனி : Moderate or heavy snow with thunder ================================================ FILE: share/translations/te-help.txt ================================================ వాడుకొనుట: $ curl wttr.in # ప్రస్తుత స్థానం $ curl wttr.in/muc # మునిక్ విమానాశ్రయంలో వాతావరణం నిర్వహించబడిన స్థాన రకాలు: /paris # పట్టనం పేరు /~Eiffel+tower # ఏదైనా ప్రదేశం (+ స్పేస్ కోసం) /Москва # ఏ భాషలోనైనా ఏదైనా స్థానం యొక్క యూనికోడ్ పేరు /muc # విమానాశ్రయం కోడ్ (3 అక్షరాలు) /@stackoverflow.com # డొమైన్ పేరు /94107 # ప్రాంతం సంకేతాలు (ఏరియా కోడ్‌లు) /-78.46,106.79 # జిపియస్ కోఆర్డినేట్‌లు చంద్రుని దశ సమాచారం: /moon # చంద్రుని దశ (,+US లేదా ,+France జోడించు ఈ నగరాల కోసం) /moon@2016-10-25 # తేదీకి చంద్ర దశ (@2016-10-25) యూనిట్లు: m # మెట్రిక్ పద్ధతి u # USCS (అమెరికా సంయుక్త రాష్ట్రాల్లో అప్రమేయంగా ఉపయోగించబడుతుంది) M # గాలి వేగం m/sలో ఎంపికలను వీక్షించండి: 0 # ప్రస్తుత వాతావరణం మాత్రమే 1 # ప్రస్తుత వాతావరణం + నేటి సూచన 2 # ప్రస్తుత వాతావరణం + నేటి మరియు రేపటి సూచన A # ANSI ఫార్మాట్‌లో అవుట్‌పుట్ F # "ఫాలో" లైన్ చూపించవద్దు n # చిన్న సంస్కరణ (పగలు మరియు రాత్రి మాత్రమే) q # చాలా చిన్న సంస్కరణ ("వాతావరణ నివేదిక" వచనం లేదు) Q # అతి చిన్న సంస్కరణ ("వాతావరణ నివేదిక" లేదు, నగరం పేరు లేదు) T # క్లోజ్ టెర్మినల్ సీక్వెన్స్ (రంగులు లేకుండా) PNG ఎంపికలు: /paris.png # PNG ఫైల్‌ను రూపొందించండి p # అవుట్‌పుట్ చుట్టూ ఫ్రేమ్‌ని జోడించండి t # పారదర్శకత 150 transparency=... # పారదర్శకత 0 నుండి 255 వరకు (255 = పారదర్శకం కాదు) background=... # నేపథ్య రంగు RRGGBB రూపంలో ఉదాహరణకు 00aaaa ఎంపికలు కలపవచ్చు: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNGలో (ఫైల్ మోడ్‌ను _ తర్వాత పేర్కొనవచ్చు) /Rome_0pq_lang=it.png # పొడవైన ఎంపికలను అండర్‌స్కోర్‌తో వేరు చేయవచ్చు స్థానికీకరణ: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris నిర్వహించబడిన భాషలు: am ar af be bn ca da de el et fr fa hi hu ia id it lt mg nb nl oc pl pt-br ro ru ta tr th uk vi zh-cn zh-tw (supported) az bg bs cy cs eo es eu fi ga hi hr hy is ja jv ka kk ko ky lv mk ml mr nl fy nn pt pt-br sk sl sr sr-lat sv sw te uz zh zu he (in progress) ప్రత్యేక URLs: /:help # ఈ పేజీని చూపుతుంది /:bash.function # సిఫార్సు చేయబడిన bash ఫంక్షన్‌ను wttr() చూపుతుంది /:translation # అనువాదకుల గురించిన సమాచారాన్ని చూపుతుంది ================================================ FILE: share/translations/te.txt ================================================ 113: : స్పష్టం 113: : ఎండ 116: : కొంత మేఘావృతం 119: : మేఘావృతం 122: : దట్టమైన మబ్బులు 143: : పొగమంచు 176: : మందమైన వర్షం సాధ్యమవుతుంది 179: : పదునైన మంచు సాధ్యం 182: : సాధ్యమయ్యే పదునైన దుప్పటి 185: : పదునైన ఘనీభవన చినుకులు సాధ్యం 200: : Thundery వ్యాప్తి సాధ్యం 227: : మంచు వెదజల్లు 230: : మంచు తుఫాను 248: : పొగమంచు 260: : చల్లటి పొగమంచు 263: : పదునైన తేలికపాటి చిల్లు 266: : కాంతి చినుకులు 281: : ఘనీభవన చినుకులు 284: : భారీ గడ్డకట్టే చినుకులు 293: : తేలికపాటి వర్షం 296: : తేలికపాటి వర్షం 299: : సమయాల్లో మితమైన వర్షం 302: : ఆధునిక వర్షం 305: : సమయాల్లో భారీ వర్షం 308: : భారీవర్షం 311: : కాంతి ఘనీభవన వర్షం 314: : ఆధునిక లేదా భారీ ఘనీభవన వర్షం 317: : కాంతి స్లేట్ 320: : ఆధునిక లేదా భారీ స్లీప్ 323: : పదునైన తేలికపాటి మంచు 326: : తేలికపాటి మంచు 329: : మందమైన మోస్తరు మంచు 332: : ఆధునిక మంచు 335: : మందమైన భారీ మంచు 338: : భారీ మంచు 350: : మంచు ముక్కలు 353: : తేలికపాటి వర్షం షవర్ 356: : ఆధునిక లేదా భారీ వర్షం షవర్ 359: : కుండపోత వర్షం షవర్ 362: : లైట్ స్లేట్ వర్షం 365: : మోడరేట్ లేదా భారీ షీట్ వర్షం 368: : తేలికపాటి మంచు వర్షం 371: : మితమైన లేదా భారీ మంచు వర్షం 386: : ఉరుములతో కూడిన తేలికపాటి వర్షం 389: : ఉరుములతో మితమైన లేదా భారీ వర్షం 392: : ఉరుములతో మందమైన తేలికపాటి మంచు 395: : ఉరుములతో మోస్తరు లేదా భారీ మంచు ================================================ FILE: share/translations/th-help.txt ================================================ วิธีใช้: $ curl wttr.in # ตำแหน่งปัจจุบัน $ curl wttr.in/muc # สภาพอากาศที่สนามบินมิวนิค รองรับสถานที่หลายรูปแบบ: /paris # ชื่อเมือง /~Eiffel+tower # สถานที่ใดก็ได้ /Москва # ชื่อ Unicode ของสถานที่และภาษาใดก็ได้ /muc # รหัสสนามบิน (3 ตัวอักษร) /@stackoverflow.com # โดเมนเนม /94107 # รหัสพื้นที่ /-78.46,106.79 # ตำแหน่ง GPS สถานที่พิเศษ: /moon # จันทรคติ (เพิ่ม ,+US หรือ ,+France สำหรับเมือง) /moon@2016-10-25 # จันทรคติ สำหรับวันที่ (@2016-10-25) หน่วย: m # เมตริก (SI) (ใช้กับทุกที่ยกเว้นประเทศอเมริกา) u # USCS (ใช้ในประเทศอเมริกา) M # แสดงความเร็วลม เมตร/วินาที ดูตัวเลือก: 0 # เฉพาะสภาพอากาศปัจจุบัน 1 # สภาพอากาศของวันพรุ่งนี้ 2 # สภาพอากาศของวันมะรืน A # ไม่สนใจ User-Agent และบังคับใช้ฟอร์แมต ANSI (terminal) F # ไม่แสดงบรรทัดติดตาม n # แสดงผลแบบแคบ (เฉพาะตอนเช้าและค่ำ) q # แสดงผลแบบเงียบ (ไม่แสดงรายงานสภาพอากาศ) Q # แสดงผลแบบเงียบที่สุด (ไม่แสดงรายงานสภาพอากาศและชื่อเมือง) T # ปิดการทำงาน terminal sequences (ไม่มีสี) ตัวเลือก PNG: /paris.png # สร้างไฟล์ PNG p # เพิ่มกรอบรอบๆไฟล์ที่สร้าง t # โปร่งใส 150 transparency=... # โปร่งใส จาก 0 ถึง 255 (255 = ไม่โปร่งใส) ตัวเลือกสามารถรวมกันได้: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # สำหรับการสร้าง PNG ให้เพิ่มตัวเลือกต่อท้าย _ /Rome_0pq_lang=it.png # ตัวเลือกหลายตัวให้ต่อด้วย _ ตัวเลือกภาษา: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris ภาษาที่รองรับ: FULL_TRANSLATION (รองรับ) PARTIAL_TRANSLATION (อยู่ระหว่างการแปล) URL พิเศษ: /:help # แสดงหน้านี้ /:bash.function # แสดงคำแนะนำ bash function wttr() /:translation # แสดงข้อมูลของผู้ร่วมแปล ================================================ FILE: share/translations/th.txt ================================================ 113: ฟ้าโปร่ง : Clear : 113: แดดจัด : Sunny : 116: มีเมฆบางส่วน : Partly cloudy : 119: มีเมฆมาก : Cloudy : 122: มืดครึ้ม : Overcast : 143: มีหมอก : Mist : 176: มีโอกาสฝนตกเป็นหย่อม ๆ : Patchy rain possible : 179: มีโอกาสหิมะตกเป็นหย่อม ๆ : Patchy snow possible : 182: มีโอกาสลูกเห็บตกเป็นหย่อม ๆ : Patchy sleet possible : 185: มีโอกาสละอองฝนเยือกแข็งตกเป็นหย่อม ๆ : Patchy freezing drizzle possible : 200: มีโอกาสฟ้าผ่า : Thundery outbreaks possible : 227: หิมะพัดผ่าน : Blowing snow : 230: พายุหิมะ : Blizzard : 248: หมอก : Fog : 260: หมอกเยือกแข็ง : Freezing fog : 263: ละอองฝนเป็นหย่อม ๆ : Patchy light drizzle : 266: ฝนพรำ : Light drizzle : 281: ละอองฝนเยือกแข็ง : Freezing drizzle : 284: ละอองฝนเยือกแข็งอย่างหนัก : Heavy freezing drizzle : 293: ฝนตกเบาบางเป็นหย่อม ๆ : Patchy light rain : 296: ฝนตกเล็กน้อย : Light rain : 299: ฝนตกปานกลางในช่วงเวลานั้น : Moderate rain at times : 302: ฝนตกปานกลาง : Moderate rain : 305: ฝนตกหนักในช่วงเวลานั้น : Heavy rain at times : 308: ฝนตกหนัก : Heavy rain : 311: ฝนเยือกแข็งเบาบาง : Light freezing rain : 314: ฝนเยือกแข็งปานกลางหรือหนัก : Moderate or heavy freezing rain : 317: ลูกเห็บเบาบาง : Light sleet : 320: ลูกเห็บปานกลางหรือหนัก : Moderate or heavy sleet : 323: หิมะตกโปรยปรายเป็นหย่อม ๆ : Patchy light snow : 326: หิมะตกเบาบาง : Light snow : 329: หิมะตกปานกลางเป็นหย่อม ๆ : Patchy moderate snow : 332: หิมะตกปานกลาง : Moderate snow : 335: หิมะตกหนักเป็นหย่อม ๆ : Patchy heavy snow : 338: หิมะตกหนัก : Heavy snow : 350: มีเกล็ดน้ำแข็ง : Ice pellets : 353: ฝนตกโปรยปราย : Light rain shower : 356: ฝนตกปานกลางหรือหนัก : Moderate or heavy rain shower : 359: ฝนตกชุก : Torrential rain shower : 362: ลูกเห็บตกเบาบาง : Light sleet showers : 365: ลูกเห็บตกปานกลางหรือหนัก : Moderate or heavy sleet showers : 368: หิมะตกโปรยปราย : Light snow showers : 371: หิมะตกปานกลางหรือหนัก : Moderate or heavy snow showers : 386: ฝนตกและมีฟ้าผ่าเป็นหย่อม ๆ : Patchy light rain with thunder : 389: ฝนตกและมีฟ้าผ่าปานกลางหรือหนัก : Moderate or heavy rain with thunder : 392: หิมะตกและมีฟ้าผ่าเป็นหย่อม ๆ : Patchy light snow with thunder : 395: หิมะตกและมีฟ้าผ่าปานกลางหรือหนัก : Moderate or heavy snow with thunder : ================================================ FILE: share/translations/tr-help.txt ================================================ Komut Satırı Kullanımı: $ curl wttr.in # bulunduğunuz konum $ curl wttr.in/esb # Esenboğa havalimanında hava durumu Desteklenen konum türleri: /istanbul # şehir adı /~Anıtkabir # herhangi bir konum (boşluk için + kullanın) /Москва # herhangi bir dildeki herhangi bir konumun Unicode adı /esb # havalimanı kodu (3 harfli) /@stackoverflow.com # etki alanı adı /06800 # posta kodları /39.925325,32.836987 # Yer belirleme sistemi (GPS) koordinatları Ay evresi bilgisi: /moon # Ay evresi (bu isimdeki şehirler için ,+US veya ,+France ekleyin) /moon@2016-10-25 # Belirli bir tarih için ay evresi (@2016-10-25) Ölçü birimleri: m # metrik sistem (SI) (ABD dışında her yer için varsayılan) u # USCS (ABD için varsayılan) M # Rüzgar hızını metre/saniye (m/s) olarak göster Seçenekleri görüntüle: 0 # yalnızca bugün için hava durumu 1 # Bugün ve 1 gün sonrası için hava durumu 2 # Bugün ve 2 gün sonrası için hava durumu A # Kullanıcı aracısı (User-Agent) bilgilerini göz ardı eder ve terminalde ANSI çıktı biçimini zorlar F # "Follow (Takip et)" satırını göstermez n # dar görünüm (yalnızca gece ve gündüz) q # sessiz görünüm ("Hava durumu" yazısı yok) Q # aşırı sessiz görünüm ("Hava durumu" yazısı ve şehir adı yok) T # terminal geçişlerini kapatır (renkler yok) PNG seçenekleri: /istanbul.png # bir PNG dosyası üretir p # çıktı etrafına bir çerçeve ekler t # 150 birim saydamlık transparency=... # 0 ila 255 arasında saydamlık (255 = saydam değil) background=... # KKYYMM biçiminde arka plan rengi, örn. 00aaaa Seçenekler birleştirilebilir: /istanbul?0pq /istanbul?0pq&lang=tr /istanbul_0pq.png # PNG dosya modları _ karakterinden sonra belirtilir /izmir_0pq_lang=tr.png # uzun seçenekler alt çizgi ile ayrılır Yerelleştirme: $ curl tr.wttr.in/istanbul $ curl wttr.in/izmir?lang=tr $ curl -H "Accept-Language: tr" wttr.in/izmir Desteklenen diller: FULL_TRANSLATION (destekleniyor) PARTIAL_TRANSLATION (geliştirme aşamasında) Özel adresler: /:help # bu sayfayı göster /:bash.function # tavsiye edilen wttr() bash fonksiyonunu göster /:translation # çevirmenler hakkındaki bilgileri göster ================================================ FILE: share/translations/tr.txt ================================================ 113: Açık : Clear 113: Güneşli : Sunny 116: Parçalı bulutlu : Partly cloudy 119: Bulutlu : Cloudy 122: Kapalı : Overcast 143: Puslu : Mist 176: Yer yer yağmur görülebilir : Patchy rain possible 179: Yer yer kar görülebilir : Patchy snow possible 182: Yer yer karla karışık yağmur görülebilir : Patchy sleet possible 185: Yer yer donan çisenti olabilir : Patchy freezing drizzle possible 200: Gök gürültülü fırtına görülebilir : Thundery outbreaks possible 227: Tipi halinde kar yağışı : Blowing snow 230: Kar fırtınası : Blizzard 248: Sisli : Fog 260: Donan sis : Freezing fog 263: Yer yer hafif donan çisenti : Patchy light drizzle 266: Yer yer donan çisenti : Light drizzle 281: Donan çisenti : Freezing drizzle 284: Kuvvetli don : Heavy freezing drizzle 293: Yer yer hafif yağmurlu : Patchy light rain 296: Hafif yağmurlu : Light rain 299: Zaman zaman orta kuvvette yağmur : Moderate rain at times 302: Orta kuvvette yağmur : Moderate rain 305: Zaman zaman şiddetli yağmur : Heavy rain at times 308: Şiddetli yağmur : Heavy rain 311: Ayaz ile hafif yağmur : Light freezing rain 314: Ayaz ile orta kuvvette veya şiddetli yağmur : Moderate or heavy freezing rain 317: Hafif karla karışık yağmur : Light sleet 320: Orta kuvvette veya şiddetli sulu kar : Moderate or heavy sleet 323: Yer yer hafif kar yağışı : Patchy light snow 326: Hafif kar yağışı : Light snow 329: Yer yer orta şiddette kar yağışı : Patchy moderate snow 332: Orta kuvvette kar yağışı : Moderate snow 335: Yer yer kuvvetli kar yağışı : Patchy heavy snow 338: Kuvvetli kar yağışı : Heavy snow 350: Dolu yağışı : Ice pellets 353: Hafif sağanak yağmur : Light rain shower 356: Orta kuvvette veya şiddetli sağanak yağmur : Moderate or heavy rain shower 359: Sel riski olan şiddetli sağanak yağmur : Torrential rain shower 362: Hafif karla karışık sağanak yağmur : Light sleet showers 365: Orta veya şiddetli karla karışık sağanak yağmur : Moderate or heavy sleet showers 368: Hafif sağanak kar yağışı : Light snow showers 371: Orta kuvvette veya şiddetli sağanak kar yağışı : Moderate or heavy snow showers 386: Yer yer gök gürültülü ve yağmurlu : Patchy light rain with thunder 389: Orta kuvvette veya şiddetli gök gürültülü yağmur : Moderate or heavy rain with thunder 392: Yer yer gök gürültülü ve karlı : Patchy light snow with thunder 395: Orta kuvvette veya şiddetli gök gürültülü kar : Moderate or heavy snow with thunder ================================================ FILE: share/translations/uk-help.txt ================================================ Використання: $ curl wttr.in # поточне місцеположення $ curl wttr.in/kbp # погода в аеропорту Бориспіль (код ICAO: KBP) Підтримуються наступні типи місцеположень: /paris # місто /~Eiffel+tower # будь-яке місцеположення /Киів # юнікодне ім'я будь-якого місцеположення будь-якою мовою /muc # код аеропорту ICAO (3 літери) /@stackoverflow.com # доменне им'я /94107 # поштовый індекс (тільки для США) /-78.46,106.79 # GPS-координати Спеціальні умовні місцеположення: /moon # Фаза Місяця (додайте ,+US або ,+France для міста Moon у США або Франції) /moon@2016-10-25 # Фаза Місяця для вказаної дати (@2016-10-25) Одиниці вимірювань: ?m # метричні (СІ) (використовуються всюди крім США) ?u # USCS (використовуються у США) ?M # показувати швидкість вітру в м/с Опції відображення: ?0 # тільки поточна погода ?1 # погода сьогодні + 1 день ?2 # погода сьогодні + 2 дня ?n # вузька версія (тільки день та ніч) ?q # тиха версія (без тексту "Прогноз погоди") ?Q # надтиха версія (без "Прогноз погоди", немає назви міста) ?T # відключити послідовності терміналу (без кольорів) PNG-опції: /paris.png # сгенерувати PNG-файл ?p # добавити рамку навколо ?t # transparency=150 (прозорість 150) transparency=... # прозорість від 0 до 255 (255 = не прозорий) Опції можна комбінувати: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # в PNG-запитах опції вказуються після знаку _ /Rome_0pq_lang=it.png # довгі опції розділяются знаком підкреслення _ Локалізація: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Мови що підтримуються: FULL_TRANSLATION (підтримується) PARTIAL_TRANSLATION (в процесі) Спеціальні строрінки: /:help # показати цю сторінку /:bash.function # показати рекомендовану функцію wttr() /:translation # показати список перекладачів wttr.in ================================================ FILE: share/translations/uk.txt ================================================ 113: Ясно : Clear : 113: Сонячно : Sunny : 116: Змінна хмарність : Partly cloudy : 119: Хмарно : Cloudy : 122: Похмуро : Overcast : 143: Невеликий туман : Mist : 176: Місцями дощ : Patchy rain possible : 179: Місцями сніг : Patchy snow possible : 182: Місцями дощ зі снігом : Patchy sleet possible : 185: Місцями ожеледь : Patchy freezing drizzle possible : 200: Місцями грози : Thundery outbreaks possible : 227: Заметіль : Blowing snow : 230: Снігова буря : Blizzard : 248: Туман : Fog : 260: Крижаний туман : Freezing fog : 263: Місцями мряка : Patchy light drizzle : 266: Мряка : Light drizzle : 281: Ожеледь : Freezing drizzle : 284: Сильна ожеледь : Heavy freezing drizzle : 293: Місцями невеликий дощ : Patchy light rain : 296: Невеликий дощ : Light rain : 299: Часом помірний дощ : Moderate rain at times : 302: Помірний дощ : Moderate rain : 305: Часом сильний дощ : Heavy rain at times : 308: Сильний дощ : Heavy rain : 311: Слабкий крижаний дощ : Light freezing rain : 314: Крижаний дощ : Moderate or heavy freezing rain : 317: Невеликий дощ зі снігом : Light sleet : 320: Дощ зі снігом : Moderate or heavy sleet : 323: Місцями невеликий сніг : Patchy light snow : 326: Невеликий сніг : Light snow : 329: Місцями помірний сніг : Patchy moderate snow : 332: Помірний сніг : Moderate snow : 335: Місцями сильний сніг : Patchy heavy snow : 338: Сильний сніг : Heavy snow : 350: Льодяний дощ : Ice pellets : 353: Невелика злива : Light rain shower : 356: Злива : Moderate or heavy rain shower : 359: Дуже сильна злива : Torrential rain shower : 362: Невеликий мокрий сніг : Light sleet showers : 365: Мокрий сніг : Moderate or heavy sleet showers : 368: Невеликий сніг : Light snow showers : 371: Сніг : Moderate or heavy snow showers : 386: Місцями дощ з грозою : Patchy light rain with thunder : 389: Дощ з грозою : Moderate or heavy rain with thunder : 392: Місцями сніг з грозою : Patchy light snow with thunder : 395: Сніг з грозою : Moderate or heavy snow with thunder : ================================================ FILE: share/translations/ukr-help.txt ================================================ Використання: $ curl wttr.in # поточне місцеположення $ curl wttr.in/kbp # погода в аеропорту Бориспіль (код ICAO: KBP) Підтримуються наступні типи місцеположень: /paris # місто /~Eiffel+tower # будь-яке місцеположення /Киів # юнікодне ім'я будь-якого місцеположення будь-якою мовою /muc # код аеропорту ICAO (3 літери) /@stackoverflow.com # доменне им'я /94107 # поштовый індекс (тільки для США) /-78.46,106.79 # GPS-координати Спеціальні умовні місцеположення: /moon # Фаза Місяця (додайте ,+US або ,+France для міста Moon у США або Франції) /moon@2016-10-25 # Фаза Місяця для вказаної дати (@2016-10-25) Одиниці вимірювань: ?m # метричні (СІ) (використовуються всюди крім США) ?u # USCS (використовуються у США) ?M # показувати швидкість вітру в м/с Опції відображення: ?0 # тільки поточна погода ?1 # погода сьогодні + 1 день ?2 # погода сьогодні + 2 дня ?n # вузька версія (тільки день та ніч) ?q # тиха версія (без тексту "Прогноз погоди") ?Q # надтиха версія (без "Прогноз погоди", немає назви міста) ?T # відключити послідовності терміналу (без кольорів) PNG-опції: /paris.png # сгенерувати PNG-файл ?p # добавити рамку навколо ?t # transparency=150 (прозорість 150) transparency=... # прозорість від 0 до 255 (255 = не прозорий) Опції можна комбінувати: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # в PNG-запитах опції вказуються після знаку _ /Rome_0pq_lang=it.png # довгі опції розділяются знаком підкреслення _ Локалізація: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Мови що підтримуються: FULL_TRANSLATION (підтримується) PARTIAL_TRANSLATION (в процесі) Спеціальні строрінки: /:help # показати цю сторінку /:bash.function # показати рекомендовану функцію wttr() /:translation # показати список перекладачів wttr.in ================================================ FILE: share/translations/uz.txt ================================================ 113: Ochiq havo : Clear : Ясно 113: Quyoshli : Sunny : Солнечно 116: Qisman bulutli : Partly cloudy : Переменная облачность 119: Bulutli : Cloudy : Облачно 122: Bulutli : Overcast : Пасмурно 143: Tuman : Mist : Дымка 176: Joylarda yomg'ir ehtimoli : Patchy rain possible : Местами дождь 179: Joylarda qor ehtimoli : Patchy snow possible : Местами снег 182: Joylarda yomg'ir aralash qor ehtimoli : Patchy sleet possible : Местами дождь со снегом 185: Joylarda muzlatuvchi mayda yomg'ir ehtimoli : Patchy freezing drizzle possible : Местами замерзающая морось 200: Momoqaldiroq ehtimoli : Thundery outbreaks possible : Местами грозы 227: Qorli izg'irin : Blowing snow : Поземок 230: Bo'ron : Blizzard : Метель 248: Tuman : Fog : Туман 260: Sovuq tuman : Freezing fog : Переохлажденный туман 263: Joylarda yengil yomg'ir : Patchy light drizzle : Местами слабая морось 266: Yengil yomg'ir : Light drizzle : Слабая морось 281: Muzlatuvchi yomg'ir : Freezing drizzle : Замерзающая морось 284: Kuchli muzlatuvchi yomg'ir : Heavy freezing drizzle : Сильная замерзающая морось 293: Joylarda yengil yomg'ir : Patchy light rain : Местами небольшой дождь 296: Yengil yomg'ir : Light rain : Небольшой дождь 299: Vaqti bilan o'rtacha yomg'ir : Moderate rain at times : Временами умеренный дождь 302: O'rtacha yomg'ir : Moderate rain : Умеренный дождь 305: Vaqti bilan kuchli yomg'ir : Heavy rain at times : Временами сильный дождь 308: Kuchli yomg'ir : Heavy rain : Сильный дождь 311: Yengil muzlatuvchi yomg'ir : Light freezing rain : Слабый переохлажденный дождь 314: O'rtacha yoki kuchli muzlatuvchi yomg'ir : Moderate or heavy freezing rain : Умеренный или сильный переохлажденный дождь 317: Yengil yomg'ir aralash qor : Light sleet : Небольшой дождь со снегом 320: O'rtacha yoki kuchli yomg'ir aralash qor : Moderate or heavy sleet : Умеренный или сильный дождь со снегом 323: Joylarda yengil qor : Patchy light snow : Местами небольшой снег 326: Yengil qor : Light snow : Небольшой снег 329: Joylarda o'rtacha qor : Patchy moderate snow : Местами умеренный снег 332: O'rtacha qor : Moderate snow : Умеренный снег 335: Joylarda kuchli qor : Patchy heavy snow : Местами сильный снег 338: Kuchli qor : Heavy snow : Сильный снег 350: Do'l : Ice pellets : Ледяной дождь 353: Qisqa jala : Light rain shower : Небольшой ливневый дождь 356: O'rtacha yoki kuchli jala : Moderate or heavy rain shower : Умеренный или сильный ливневый дождь 359: Shiddatli jala : Torrential rain shower : Очень сильный ливень 362: Qisqa qorli jala : Light sleet showers : Небольшой ливневый дождь со снегом 365: O'rtacha yoki kuchli qorli jala : Moderate or heavy sleet showers : Небольшой снег 368: Qisqa qor aralash yomg'ir : Light snow showers : Небольшой ливневый дождь со снегом 371: O'rtacha yoki kuchli qorli jala : Moderate or heavy snow showers : Умеренный или сильный снежный ливень 386: Joylarda yengil yomg'ir va momaqaldiroq : Patchy light rain with thunder : Местами небольшой дождь с грозой 389: O'rtacha yoki kuchli yomg'ir va momaqaldiroq : Moderate or heavy rain with thunder : Умеренный или сильный дождь с грозой 392: Joylarda yengil qor va momaqaldiroq : Patchy light snow with thunder : Местами небольшой снег с грозой 395: O'rtacha yoku kuchli qor va momaqaldiroq : Moderate or heavy snow with thunder : Умеренный или сильный снег с грозой ================================================ FILE: share/translations/vi-help.txt ================================================ Cách dùng: $ curl wttr.in # vị trí hiện tại $ curl wttr.in/sgn # thời tiết sân bay Tân Sơn Nhất Các loại địa điểm được hỗ trợ: /paris # tên thành phố /~Eiffel+tower # địa điểm nổi tiếng bất kỳ /Москва # tên địa điểm Unicode trong bất cứ ngôn ngữ nào /sgn # mã sân bay IATA (3 ký tự) /@stackoverflow.com # tên miền /94107 # mã vùng (chỉ cho Hoa Kỳ) /-78.46,106.79 # tọa độ Địa điểm đặc biệt: /moon # pha mặt trăng (thêm ,+US hoặc ,+France để lấy chu kỳ mặt trăng cho địa điểm đó) /moon@2016-10-25 # pha mặt trăng của 1 ngày (@2016-10-25) Đơn vị: ?m # hệ mét (SI) (mặc định cho mọi nơi trừ Mỹ) ?u # USCS (mặc định cho Mỹ) ?M # tốc độ gió theo m/s Tùy chọn hiển thị: ?0 # Chỉ hiện thời tiết hiện tại ?1 # Thời tiết hiện tại + 1 ngày ?2 # Thời tiết hiện tại + 2 ngày ?n # bản rút gọn (chỉ có ngày & đêm) ?q # bản thu nhỏ (không có dòng "Báo cáo thời tiết") ?Q # bản siêu nhỏ (không có dòng "Báo cáo thời tiết" và địa điểm) ?T # tắt escape sequence cho terminal (không màu) Tùy chọn PNG: /paris.png # tạo ra file PNG ?p # thêm khung xung quanh ?t # độ trong suốt = 150 transparency=... # độ trong suốt từ 0 tới 255 (255 = không trong suốt) Kết hợp các tùy chọn: /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # với file PNG thì tùy chọn nằm phía sau dấu _ /Rome_0pq_lang=it.png # các tùy chọn phân cách với nhau bằng dấu _ Bản địa hóa: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris Các ngôn ngữ được hỗ trợ: FULL_TRANSLATION (hỗ trợ) PARTIAL_TRANSLATION (chưa dịch hoàn toàn) URL đặc biệt: /:help # hiện trang này /:bash.function # hiện hàm wttr() được đề nghị cho bash /:translation # hiện thông tin về người biên dịch ================================================ FILE: share/translations/vi.txt ================================================ 113: Trời trong : Clear 113: Trời nắng : Sunny 116: Có mây : Partly cloudy 119: Nhiều mây : Cloudy 122: Âm u : Overcast 143: Sương mù : Mist 176: Có mưa rải rác : Patchy rain possible 179: Có tuyết rải rác : Patchy snow possible 182: Có mưa đá rải rác : Patchy sleet possible 185: Có mưa phùn băng rải rác : Patchy freezing drizzle possible 200: Có dông : Thundery outbreaks possible 227: Tuyết bay : Blowing snow 230: Bão tuyết : Blizzard 248: Sương mù : Fog 260: Sương mù đóng băng : Freezing fog 263: Mưa phùn nhẹ rải rác : Patchy light drizzle 266: Mưa phùn nhẹ : Light drizzle 281: Mưa phùn băng : Freezing drizzle 284: Mưa phùn băng to : Heavy freezing drizzle 293: Mưa nhẹ rải rác : Patchy light rain 296: Mưa nhỏ : Light rain 299: Thỉnh thoảng có mưa vừa : Moderate rain at times 302: Mưa vừa : Moderate rain 305: Thỉnh thoảng có mưa to : Heavy rain at times 308: Mưa to : Heavy rain 311: Mưa băng giá nhẹ : Light freezing rain 314: Mưa băng giá vừa đến to : Moderate or heavy freezing rain 317: Mưa đá nhỏ : Light sleet 320: Mưa đá vừa đến to : Moderate or heavy sleet 323: Tuyết rơi nhẹ rải rác : Patchy light snow 326: Tuyết rơi nhẹ : Light snow 329: Tuyết rơi vừa rải rác : Patchy moderate snow 332: Tuyết rơi vừa : Moderate snow 335: Tuyết rơi dày rải rác : Patchy heavy snow 338: Tuyết rơi dày : Heavy snow 350: Mưa đá : Ice pellets 353: Mưa rào nhẹ : Light rain shower 356: Mưa rào vừa đến to : Moderate or heavy rain shower 359: Mưa rào xối xả : Torrential rain shower 362: Mưa rào đá nhẹ : Light sleet showers 365: Mưa rào đá vừa đến to : Moderate or heavy sleet showers 368: Mưa rào tuyết nhỏ : Light snow showers 371: Mưa rào tuyết vừa đến to : Moderate or heavy snow showers 386: Mưa rào nhẹ có dông rải rác : Patchy light rain with thunder 389: Mưa dông vừa đến to : Moderate or heavy rain with thunder 392: Tuyết rơi nhẹ có dông rải rác : Patchy light snow with thunder 395: Tuyết rơi vừa đến dày có dông rải rác : Moderate or heavy snow with thunder ================================================ FILE: share/translations/zh-cn-help.txt ================================================ 用法: $ curl wttr.in # 当前位置 $ curl wttr.in/pek # 机场三字码 支持的位置类型: /paris # 城市名称 /~Eiffel+tower # 任意地点 /北京 # 任意语言的任意地点 /PEK # 机场三字码 /@stackoverflow.com # 域名 /94107 # 区域代码 /-78.46,106.79 # GPS 坐标 特殊位置: /moon # 月相(加上 ,+US 或 ,+France 以查看特定城市的月相) /moon@2016-10-25 # 特定日期的月相(@2016-10-25) 单位: m # 公制(国际单位制)(除美国外该单位制为默认值) u # USCS(美国默认单位制) M # 以米/秒为单位显示风速 查看选项: 0 # 仅当前天气 1 # 当前天气 + 未来 1 天 2 # 当前天气 + 未来 2 天 A # 忽略用户代理(User-Agent)并强制使用 ANSI 输出格式(终端模式) F # 不显示「关注」(Follow)那行 n # 较窄模式(仅日夜) q # 简练模式(无「天气预报」文字) Q # 极简模式(无「天气预报」、无城市名称) T # 关闭终端转义序列(无颜色) PNG 选项: /paris.png # 生成 PNG 文件 p # 在输出图像周围增加缩进空白 t # 透明度 150 transparency=... # 透明度从 0 到 255(255 = 不透明) 可合并使用的选项 /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNG 文件模式中在下划线 _ 后指定选项 /Rome_0pq_lang=it.png # 用下划线分隔长选项 本地化: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris 支持的语言: FULL_TRANSLATION (supported) PARTIAL_TRANSLATION (in progress) 特殊 URL: /:help # 显示此页面 /:bash.function # 显示建议的 bash 函数 wttr() /:translation # 显示翻译者信息 ================================================ FILE: share/translations/zh-cn.txt ================================================ 113:晴 : Clear 113:晴 : Sunny 116:少云 : Partly cloudy 119:多云 : Cloudy 122:阴 : Overcast 143:轻雾 : Mist 176:局部下小雨 : Patchy rain possible 179:局部下小雪 : Patchy snow possible 182:局部有雨夹雪 : Patchy sleet possible 185:局部有冻毛毛雨 : Patchy freezing drizzle possible 200:可能打雷 : Thundery outbreaks possible 227:小雪 : Blowing snow 230:暴雪 : Blizzard 248:雾 : Fog 260:冻雾 : Freezing fog 263:局部毛毛雨 : Patchy light drizzle 266:毛毛雨 : Light drizzle 281:冻毛毛雨 : Freezing drizzle 284:大冻毛毛雨 : Heavy freezing drizzle 293:局部小雨 : Patchy light rain 296:小雨 : Light rain 299:有时中雨 : Moderate rain at times 302:中雨 : Moderate rain 305:有时大雨 : Heavy rain at times 308:大雨 : Heavy rain 311:小冻雨 : Light freezing rain 314:中或大冻雨 : Moderate or heavy freezing rain 317:小雨夹雪 : Light sleet 320:中或大雨夹雪 : Moderate or heavy sleet 323:局部小雪 : Patchy light snow 326:小雪 : Light snow 329:局部中雪 : Patchy moderate snow 332:中雪 : Moderate snow 335:局部大雪 : Patchy heavy snow 338:大雪 : Heavy snow 350:冰丸 : Ice pellets 353:小阵雨 : Light rain shower 356:中或大阵雨 : Moderate or heavy rain shower 359:暴阵雨 : Torrential rain shower 362:小阵雨夹雪 : Light sleet showers 365:中或大阵雨夹雪 : Moderate or heavy sleet showers 368:小阵雪 : Light snow showers 371:中或大阵雪 : Moderate or heavy snow showers 386:局部小雷阵雨 : Patchy light rain with thunder 389:中或大雷阵雨 : Moderate or heavy rain with thunder 392:小雷阵雪 : Patchy light snow with thunder 395:中或大雷阵雪 : Moderate or heavy snow with thunder ================================================ FILE: share/translations/zh-tw-help.txt ================================================ 使用方法 $ curl wttr.in # 目前位置 $ curl wttr.in/muc # 慕尼黑機場的天氣 支援的位置類型: /paris # 城市名稱 /~Eiffel+tower # 任意地點 /Москва # 任意地點在任何語言的 Unicode 名稱 /muc # 機場代碼(3個字母) /@stackoverflow.com # 網域名稱 /94107 # 區域代碼 /-78.46,106.79 # GPS 座標 特殊位置: /moon # 月相(加入 ,+US 或 ,+France 來觀看這些地方的月相) /moon@2016-10-25 # 特定日期的月相 (@2016-10-25) 單位: m # 公制(國際單位制)(除了美國以外的地方皆預設使用此單位制) u # USCS(美國預設使用) M # 以公尺/秒為單位顯示風速 檢視選項: 0 # 僅目前的天氣 1 # 目前的天氣+未來1天 2 # 目前的天氣+未來2天 A # 忽略使用者代理字串並強制使用 ANSI 輸出格式(終端機) F # 不要顯示「追蹤」那行 n # 窄版(僅日夜) q # 安靜版(無「天氣報告」文字) Q # 超安靜版(無「天氣報告」、無城市名稱) T # 關閉終端機序列(無色彩) PNG 選項: /paris.png # 生成 PNG 檔案 p # 在輸出周圍新增框架 t # 透明度 150 transparency=... # 透明度從 0 到 255(255 = 不透明) 可合併使用的選項 /Paris?0pq /Paris?0pq&lang=fr /Paris_0pq.png # PNG 檔案模式於 _ 後指定 /Rome_0pq_lang=it.png # 以底線分隔長選項 在地化: $ curl fr.wttr.in/Paris $ curl wttr.in/paris?lang=fr $ curl -H "Accept-Language: fr" wttr.in/paris 支援的語言: FULL_TRANSLATION (supported) PARTIAL_TRANSLATION (in progress) 特殊 URL: /:help # 顯示此頁面 /:bash.function # 顯示建議的 bash 函式 wttr() /:translation # 顯示關於譯者的資訊 ================================================ FILE: share/translations/zh-tw.txt ================================================ 113: 晴朗 : Clear 113: 陽光充足 : Sunny 116: 晴時多雲 : Partly cloudy 119: 多雲 : Cloudy 122: 陰天 : Overcast 143: 薄霧 : Mist 176: 可能下小雨 : Patchy rain possible 179: 可能下小雪 : Patchy snow possible 182: 可能有雨夾雪 : Patchy sleet possible 185: 可能有凍毛毛雨 : Patchy freezing drizzle possible 200: 可能打雷 : Thundery outbreaks possible 227: 吹雪 : Blowing snow 230: 雪暴 : Blizzard 248: 霧 : Fog 260: 凍霧 : Freezing fog 263: 局部毛毛細雨 : Patchy light drizzle 266: 毛毛細雨 : Light drizzle 281: 凍毛毛雨 : Freezing drizzle 284: 重凍毛毛雨 : Heavy freezing drizzle 293: 局部小雨 : Patchy light rain 296: 小雨 : Light 299: 有時中雨 : Moderate rain at times 302: 中雨 : Moderate rain 305: 有時大雨 : Heavy rain at times 308: 大雨 : Heavy rain 311: 小凍雨 : Light freezing rain 314: 中或大凍雨 : Moderate or heavy freezing rain 317: 小雨夾雪 : Light sleet 320: 中或大雨夾雪 : Moderate or heavy sleet 323: 局部小雪 : Patchy light snow 326: 小雪 : Light snow 329: 局部中雪 : Patchy moderate snow 332: 中雪 : Moderate snow 335: 局部大雪 : Patchy heavy snow 338: 大雪 : Heavy snow 350: 冰珠 : Ice pellets 353: 小陣雨 : Light rain shower 356: 中或大陣雨 : Moderate or heavy rain shower 359: 豪陣雨 : Torrential rain shower 362: 小陣雨夾雪 : Light sleet showers 365: 中或大陣雨夾雪 : Moderate or heavy sleet showers 368: 小陣雪 : Light snow showers 371: 中或大陣雪 : Moderate or heavy snow showers 386: 局部小雷雨 : Patchy light rain with thunder 389: 中或大雷雨 : Moderate or heavy rain with thunder 392: 可能會打雷的小陣雪 : Patchy light snow with thunder 395: 可能會打雷的中或大陣雪 : Moderate or heavy snow with thunder ================================================ FILE: spec/options/options.yaml ================================================ query_options: - name: current_only short: "0" description: "Show only current weather" type: boolean default: false active: true - name: current_plus_today short: "1" description: "Show current weather and today's forecast" type: boolean default: false active: true - name: current_plus_two_days short: "2" description: "Show current weather, today's, and tomorrow's forecast" type: boolean default: false active: true - name: current_plus_three_days short: "3" description: "Show current weather, forecast for today, tomorrow, and day after" type: boolean default: false active: true - name: ansi_output short: A description: "Force ANSI output format for terminals" type: boolean default: false active: true - name: standard_font short: d description: "Restrict output to standard console font glyphs" type: boolean default: false active: true - name: no_follow short: F description: "Do not show the 'Follow' line" type: boolean default: false active: true - name: metric short: m description: "Use metric (SI) units (default everywhere except US)" type: boolean values: - true - false default: true active: true note: "Overridden by 'uscs' if specified; default is 'uscs' in US" - name: uscs short: u description: "Use USCS units (default in the US)" type: boolean values: - true - false default: false active: true note: "Default is 'uscs' in US, 'metric' elsewhere" - name: wind_ms short: M description: "Show wind speed in meters per second" type: boolean default: false active: true - name: narrow short: n description: "Show only day and night forecast (narrow version)" type: boolean default: false active: true - name: frame short: p description: "Add a frame around the PNG output" type: boolean default: false active: true note: "Applicable only for PNG output" - name: quiet short: q description: "Quiet version, no 'Weather report' text" type: boolean default: false active: true - name: superquiet short: Q description: "Superquiet version, no 'Weather report' text or city name" type: boolean default: false active: true - name: period type: integer description: "Update interval for cyclic location selection" default: null active: true - name: random type: integer description: "Random number, used to bypass the first caching layer (alias: dummy, nonce)" active: true - name: dummy type: integer description: "Random number, used to bypass the first caching layer (alias: random, nonce)" active: true - name: nonce type: integer description: "Random number, used to bypass the first caching layer (alias: dummy, random)" active: true - name: no_color short: T description: "Switch off terminal color sequences (no colors)" type: boolean default: false active: true - name: lang description: "Specify output language" type: string values_map: am: "Amharic" ar: "Arabic" af: "Afrikaans" be: "Belarusian" bn: "Bengali" ca: "Catalan" da: "Danish" de: "German" el: "Greek" et: "Estonian" en: "English" fr: "French" fa: "Persian" gl: "Galician" hi: "Hindi" hu: "Hungarian" ia: "Interlingua" id: "Indonesian" it: "Italian" lt: "Lithuanian" mg: "Malagasy" nb: "Norwegian Bokmål" nl: "Dutch" oc: "Occitan" pl: "Polish" pt-br: "Portuguese (Brazil)" ro: "Romanian" ru: "Russian" ta: "Tamil" tr: "Turkish" th: "Thai" ua: "Ukrainian" vi: "Vietnamese" zh-cn: "Chinese (Simplified)" zh-tw: "Chinese (Traditional)" az: "Azerbaijani (partial support)" ba: "Bashkir (partial support)" bg: "Bulgarian (partial support)" cy: "Welsh (partial support)" bs: "Bosnian (partial support)" by: "Belarusian (partial support)" cz: "Czech (partial support)" eo: "Esperanto (partial support)" es: "Spanish (partial support)" eu: "Basque (partial support)" fi: "Finnish (partial support)" ga: "Irish (partial support)" hr: "Croatian (partial support)" hy: "Armenian (partial support)" is: "Icelandic (partial support)" ja: "Japanese (partial support)" jv: "Javanese (partial support)" ka: "Georgian (partial support)" kk: "Kazakh (partial support)" ko: "Korean (partial support)" ky: "Kyrgyz (partial support)" lv: "Latvian (partial support)" mk: "Macedonian (partial support)" ml: "Malayalam (partial support)" mr: "Marathi (partial support)" fy: "Frisian (partial support)" nn: "Norwegian Nynorsk (partial support)" pt: "Portuguese (partial support)" sk: "Slovak (partial support)" sl: "Slovenian (partial support)" sr: "Serbian (partial support)" sr-lat: "Serbian (Latin) (partial support)" sv: "Swedish (partial support)" sw: "Swahili (partial support)" te: "Telugu (partial support)" ts: "Tsonga (partial support)" zu: "Zulu (partial support)" he: "Hebrew (partial support)" zh: "Chinese (partial support)" default: en active: true note: "Languages from 'az' to 'zh' have partial support; others have full support" - name: background description: "Set background color for PNG output in hex (RRGGBB format)" type: string default: null validate: - "length 6" - "regexp [0-9a-fA-F]{6}" active: true note: "Applicable only for PNG output" - name: format description: "Specify output format for one-line weather info" type: string # values_map: # "1": "Location and condition" # "2": "Location, condition, temperature" # "3": "Location: condition temperature" # "4": "Location: condition, temperature details, wind speed" # custom: "Percent notation (e.g., %l, %c, %t, %w, %m, etc.)" # values: # - "1" # - "2" # - "3" # - "4" # - "custom" default: null active: true note: "Used for status bars in tools like tmux" - name: date description: "Specify forecast date offset (days in the future)" type: integer range: min: 1 max: null default: 0 active: false note: "Proposed but not officially supported; limited evidence" - name: force-ansi short: A description: "Force ANSI output mode" type: boolean default: false active: true note: "Enables ANSI formatting regardless of terminal capabilities" - name: dumb short: d description: "Use dumb terminal mode" type: boolean default: false active: true note: "Disables advanced terminal features" - name: narrow short: n description: "Use narrow layout for output" type: boolean default: false active: true note: "Optimizes output for narrow displays" - name: use_metric short: m description: "Use metric units for measurements" type: boolean default: false active: true note: "Sets units to metric system; 'M' also enables milliseconds for wind speed" - name: use_ms_for_wind short: M description: "Use milliseconds for wind speed (requires metric units)" type: boolean default: false active: true note: "Only active when used with 'M' flag for metric units" - name: use_imperial short: u description: "Use imperial units for measurements" type: boolean default: false active: true note: "Sets units to imperial system" - name: inverted_colors short: I description: "Invert colors in output" type: boolean default: false active: true note: "Swaps foreground and background colors" - name: transparency short: t description: "Set transparency level for PNG output" type: integer range: min: 0 max: 255 default: 150 active: true note: "Applicable only for PNG output; 255 = not transparent" - name: no-terminal short: T description: "Disable terminal-specific formatting" type: boolean default: false active: true note: "Output is not tailored to terminal environment" - name: padding short: p description: "Add padding to output" type: boolean default: false active: true note: "Adds extra spacing around output content" - name: days description: "Set the number of forecast days to display" type: integer range: min: 0 max: 3 default: null active: true note: "Limits forecast output to the specified number of days" - name: no-caption short: q description: "Disable captions in output" type: boolean default: false active: true note: "Removes descriptive text or labels from output" - name: no-city short: Q description: "Hide city name in output" type: boolean default: false active: true note: "Omits location or city name from display" - name: no-follow-line short: F description: "Disable follow line feature" type: boolean default: false active: true note: "Prevents automatic line following in output" - name: question_mark short: "?" description: "Workaround for multiple usage of the question mark in the query" type: boolean default: false active: true format_specifiers: - specifier: "%c" description: "Weather condition" active: true - specifier: "%C" description: "Weather condition text" active: true - specifier: "%h" description: "Humidity" active: true - specifier: "%t" description: "Temperature" active: true - specifier: "%w" description: "Wind" active: true - specifier: "%l" description: "Location" active: true - specifier: "%m" description: "Moon phase" active: true - specifier: "%M" description: "Moon day" active: true - specifier: "%p" description: "Precipitation" active: true - specifier: "%D" description: "Dawn" active: true - specifier: "%S" description: "Sunrise" active: true - specifier: "%z" description: "Zenith" active: true - specifier: "%s" description: "Sunset" active: true - specifier: "%d" description: "Dusk" active: true - specifier: "%H" description: "High temperature for the day" active: false note: "Proposed in issue #585" - specifier: "%L" description: "Low temperature for the day" active: false note: "Proposed in issue #585" ================================================ FILE: srv.go ================================================ package main import ( "crypto/tls" "fmt" "io" stdlog "log" "net/http" "time" "github.com/alecthomas/kong" log "github.com/sirupsen/logrus" "github.com/chubin/wttr.in/internal/config" geoip "github.com/chubin/wttr.in/internal/geo/ip" geoloc "github.com/chubin/wttr.in/internal/geo/location" "github.com/chubin/wttr.in/internal/logging" "github.com/chubin/wttr.in/internal/options" "github.com/chubin/wttr.in/internal/processor" "github.com/chubin/wttr.in/internal/types" // v1 "github.com/chubin/wttr.in/internal/view/v1" ) //nolint:gochecknoglobals var cli struct { ConfigFile string `name:"config-file" arg:"" optional:"" help:"Name of configuration file"` ConfigCheck bool `name:"config-check" help:"Check configuration"` ConfigDump bool `name:"config-dump" help:"Dump configuration"` ConvertGeoIPCache bool `name:"convert-geo-ip-cache" help:"Convert Geo IP data cache to SQlite"` ConvertGeoLocationCache bool `name:"convert-geo-location-cache" help:"Convert Geo Location data cache to SQlite"` GeoResolve string `name:"geo-resolve" help:"Resolve location"` LogLevel string `name:"log-level" short:"l" help:"Show log messages with level" default:"info"` CheckQueriesInLog string `name:"check-queries-in-log" help:"Check queries in the log file"` OutputLog string `name:"output-log" help:"Output log"` // V1 v1.Configuration } const logLineStart = "LOG_LINE_START " func suppressMessages() []string { return []string{ "error reading preface from client", "TLS handshake error from", "URL query contains semicolon, which is no longer a supported separator", "connection error: PROTOCOL_ERROR", } } func copyHeader(dst, src http.Header) { for k, vv := range src { for _, v := range vv { dst.Add(k, v) } } } func serveHTTP(mux *http.ServeMux, port int, logFile io.Writer, errs chan<- error) { srv := &http.Server{ Addr: fmt.Sprintf(":%d", port), ErrorLog: stdlog.New(logFile, logLineStart, stdlog.LstdFlags), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 1 * time.Second, Handler: mux, } errs <- srv.ListenAndServe() } func serveHTTPS(mux *http.ServeMux, port int, certFile, keyFile string, logFile io.Writer, errs chan<- error) { tlsConfig := &tls.Config{ // CipherSuites: []uint16{ // tls.TLS_CHACHA20_POLY1305_SHA256, // tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, // tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, // }, // MinVersion: tls.VersionTLS13, } srv := &http.Server{ Addr: fmt.Sprintf(":%d", port), ErrorLog: stdlog.New(logFile, logLineStart, stdlog.LstdFlags), ReadTimeout: 5 * time.Second, WriteTimeout: 20 * time.Second, IdleTimeout: 1 * time.Second, TLSConfig: tlsConfig, Handler: mux, } errs <- srv.ListenAndServeTLS(certFile, keyFile) } func serve(conf *config.Config) error { var ( // mux is main HTTP/HTTP requests multiplexer. mux = http.NewServeMux() // logger is optimized requests logger. logger = logging.NewRequestLogger( conf.Logging.AccessLog, time.Duration(conf.Logging.Interval)*time.Second) rp *processor.RequestProcessor // errs is the servers errors channel. errs = make(chan error, 1) // numberOfServers started. If 0, exit. numberOfServers int errorsLog = logging.NewLogSuppressor( conf.Logging.ErrorsLog, suppressMessages(), logLineStart, ) err error ) rp, err = processor.NewRequestProcessor(conf) if err != nil { return fmt.Errorf("log processor initialization: %w", err) } err = errorsLog.Open() if err != nil { return err } err = rp.Start() if err != nil { return err } mux.HandleFunc("/", mainHandler(rp, logger)) if conf.Server.PortHTTP != 0 { go serveHTTP(mux, conf.Server.PortHTTP, errorsLog, errs) numberOfServers++ } if conf.Server.PortHTTPS != 0 { go serveHTTPS(mux, conf.Server.PortHTTPS, conf.Server.TLSCertFile, conf.Server.TLSKeyFile, errorsLog, errs) numberOfServers++ } if numberOfServers == 0 { return types.ErrNoServersConfigured } return <-errs // block until one of the servers writes an error } func mainHandler( rp *processor.RequestProcessor, logger *logging.RequestLogger, ) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { if err := logger.Log(r); err != nil { log.Println(err) } response, err := rp.ProcessRequest(r) if err != nil { log.Println(err) return } if response.StatusCode == 0 { log.Println("status code 0", response) return } copyHeader(w.Header(), response.Header) w.Header().Set("Access-Control-Allow-Origin", "*") w.WriteHeader(response.StatusCode) _, err = w.Write(response.Body) if err != nil { log.Println(err) } } } func main() { var ( conf *config.Config err error ) ctx := kong.Parse(&cli) ctx.FatalIfErrorf(setLogLevel(cli.LogLevel)) if cli.ConfigFile != "" { conf, err = config.Load(cli.ConfigFile) if err != nil { log.Fatalf("reading config from %s: %s\n", cli.ConfigFile, err) } } else { conf = config.Default() } if cli.ConfigDump { //nolint:forbidigo fmt.Print(string(conf.Dump())) return } if cli.ConfigCheck { return } switch { case cli.ConvertGeoIPCache: ctx.FatalIfErrorf(convertGeoIPCache(conf)) case cli.ConvertGeoLocationCache: ctx.FatalIfErrorf(convertGeoLocationCache(conf)) case cli.CheckQueriesInLog != "": if cli.OutputLog == "" { ctx.Fatalf("missing output log (--output-log)") } wttrinOptions, err := options.NewFromFile("spec/options/options.yaml") ctx.FatalIfErrorf(err) err = options.ProcessLogFile(cli.CheckQueriesInLog, cli.OutputLog, wttrinOptions) ctx.FatalIfErrorf(err) case cli.GeoResolve != "": sr := geoloc.NewSearcher(conf) loc, err := sr.Search(cli.GeoResolve) ctx.FatalIfErrorf(err) if loc != nil { //nolint:forbidigo fmt.Println(*loc) } default: err = serve(conf) ctx.FatalIfErrorf(err) } } func convertGeoIPCache(conf *config.Config) error { geoIPCache, err := geoip.NewCache(conf) if err != nil { return err } return geoIPCache.ConvertCache() } func convertGeoLocationCache(conf *config.Config) error { geoLocCache, err := geoloc.NewCache(conf) if err != nil { return err } return geoLocCache.ConvertCache(false) } func setLogLevel(logLevel string) error { parsedLevel, err := log.ParseLevel(logLevel) if err != nil { return err } log.SetLevel(parsedLevel) return nil } ================================================ FILE: test/proxy-data/data1 ================================================ {"data":{"request":[{"type":"LatLon","query":"Lat 27.64 and Lon -80.40"}],"nearest_area":[{"areaName":[{"value":"Vero Beach"}],"country":[{"value":"United States of America"}],"region":[{"value":"Florida"}],"latitude":"27.638","longitude":"-80.398","population":"17262","weatherUrl":[{"value":"https://www.worldweatheronline.com/v2/weather.aspx?q=27.6387163,-80.3975399"}]}],"current_condition":[{"observation_time":"10:24 AM","localObsDateTime":"2020-04-26 06:24 AM","temp_C":"22","temp_F":"72","weatherCode":"143","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0006_mist.png"}],"weatherDesc":[{"value":"Mist"}],"windspeedMiles":"4","windspeedKmph":"6","winddirDegree":"240","winddir16Point":"WSW","precipMM":"0.4","humidity":"94","visibility":"10","pressure":"1015","cloudcover":"75","FeelsLikeC":"25","FeelsLikeF":"76","uvIndex":1}],"weather":[{"date":"2020-04-26","astronomy":[{"sunrise":"06:46 AM","sunset":"07:53 PM","moonrise":"09:12 AM","moonset":"11:17 PM","moon_phase":"Waxing Crescent","moon_illumination":"22"}],"maxtempC":"30","maxtempF":"87","mintempC":"23","mintempF":"73","totalSnow_cm":"0.0","sunHour":"6.5","uvIndex":"9","hourly":[{"time":"0","tempC":"25","tempF":"77","windspeedMiles":"7","windspeedKmph":"12","winddirDegree":"235","winddir16Point":"SW","weatherCode":"176","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0025_light_rain_showers_night.png"}],"weatherDesc":[{"value":"Patchy rain possible"}],"precipMM":"0.4","humidity":"85","visibility":"9","pressure":"1015","cloudcover":"86","HeatIndexC":"28","HeatIndexF":"82","DewPointC":"22","DewPointF":"72","WindChillC":"25","WindChillF":"77","WindGustMiles":"14","WindGustKmph":"22","FeelsLikeC":"28","FeelsLikeF":"82","chanceofrain":"76","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"89","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"71","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"300","tempC":"24","tempF":"76","windspeedMiles":"7","windspeedKmph":"12","winddirDegree":"242","winddir16Point":"WSW","weatherCode":"386","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0032_thundery_showers_night.png"}],"weatherDesc":[{"value":"Patchy light rain with thunder"}],"precipMM":"0.4","humidity":"88","visibility":"9","pressure":"1015","cloudcover":"81","HeatIndexC":"27","HeatIndexF":"80","DewPointC":"22","DewPointF":"72","WindChillC":"24","WindChillF":"76","WindGustMiles":"13","WindGustKmph":"21","FeelsLikeC":"27","FeelsLikeF":"80","chanceofrain":"70","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"87","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"13","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"600","tempC":"24","tempF":"75","windspeedMiles":"7","windspeedKmph":"12","winddirDegree":"246","winddir16Point":"WSW","weatherCode":"386","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0032_thundery_showers_night.png"}],"weatherDesc":[{"value":"Patchy light rain with thunder"}],"precipMM":"0.6","humidity":"89","visibility":"10","pressure":"1015","cloudcover":"74","HeatIndexC":"26","HeatIndexF":"79","DewPointC":"22","DewPointF":"72","WindChillC":"24","WindChillF":"75","WindGustMiles":"12","WindGustKmph":"20","FeelsLikeC":"26","FeelsLikeF":"79","chanceofrain":"77","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"87","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"12","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"900","tempC":"26","tempF":"78","windspeedMiles":"7","windspeedKmph":"12","winddirDegree":"246","winddir16Point":"WSW","weatherCode":"386","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0016_thundery_showers.png"}],"weatherDesc":[{"value":"Patchy light rain with thunder"}],"precipMM":"1.1","humidity":"83","visibility":"10","pressure":"1017","cloudcover":"80","HeatIndexC":"28","HeatIndexF":"83","DewPointC":"22","DewPointF":"72","WindChillC":"26","WindChillF":"78","WindGustMiles":"11","WindGustKmph":"17","FeelsLikeC":"28","FeelsLikeF":"83","chanceofrain":"73","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"88","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"42","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"9"},{"time":"1200","tempC":"29","tempF":"84","windspeedMiles":"7","windspeedKmph":"12","winddirDegree":"247","winddir16Point":"WSW","weatherCode":"353","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0009_light_rain_showers.png"}],"weatherDesc":[{"value":"Light rain shower"}],"precipMM":"0.7","humidity":"68","visibility":"10","pressure":"1017","cloudcover":"76","HeatIndexC":"32","HeatIndexF":"90","DewPointC":"22","DewPointF":"72","WindChillC":"29","WindChillF":"84","WindGustMiles":"9","WindGustKmph":"15","FeelsLikeC":"32","FeelsLikeF":"90","chanceofrain":"74","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"85","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"95","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"31","uvIndex":"9"},{"time":"1500","tempC":"30","tempF":"86","windspeedMiles":"7","windspeedKmph":"11","winddirDegree":"259","winddir16Point":"WSW","weatherCode":"386","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0016_thundery_showers.png"}],"weatherDesc":[{"value":"Patchy light rain with thunder"}],"precipMM":"1.0","humidity":"63","visibility":"10","pressure":"1016","cloudcover":"62","HeatIndexC":"33","HeatIndexF":"92","DewPointC":"22","DewPointF":"72","WindChillC":"30","WindChillF":"86","WindGustMiles":"9","WindGustKmph":"14","FeelsLikeC":"33","FeelsLikeF":"92","chanceofrain":"76","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"87","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"96","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"87","uvIndex":"9"},{"time":"1800","tempC":"28","tempF":"82","windspeedMiles":"6","windspeedKmph":"9","winddirDegree":"259","winddir16Point":"WSW","weatherCode":"386","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0016_thundery_showers.png"}],"weatherDesc":[{"value":"Patchy light rain with thunder"}],"precipMM":"1.5","humidity":"70","visibility":"10","pressure":"1015","cloudcover":"72","HeatIndexC":"31","HeatIndexF":"88","DewPointC":"22","DewPointF":"71","WindChillC":"28","WindChillF":"82","WindGustMiles":"9","WindGustKmph":"15","FeelsLikeC":"31","FeelsLikeF":"88","chanceofrain":"72","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"86","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"93","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"48","uvIndex":"9"},{"time":"2100","tempC":"25","tempF":"77","windspeedMiles":"7","windspeedKmph":"11","winddirDegree":"243","winddir16Point":"WSW","weatherCode":"176","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0025_light_rain_showers_night.png"}],"weatherDesc":[{"value":"Patchy rain possible"}],"precipMM":"0.2","humidity":"74","visibility":"10","pressure":"1016","cloudcover":"75","HeatIndexC":"27","HeatIndexF":"81","DewPointC":"20","DewPointF":"68","WindChillC":"25","WindChillF":"77","WindGustMiles":"13","WindGustKmph":"21","FeelsLikeC":"27","FeelsLikeF":"81","chanceofrain":"81","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"90","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"61","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"}]},{"date":"2020-04-27","astronomy":[{"sunrise":"06:45 AM","sunset":"07:54 PM","moonrise":"09:58 AM","moonset":"No moonset","moon_phase":"Waxing Crescent","moon_illumination":"30"}],"maxtempC":"24","maxtempF":"76","mintempC":"19","mintempF":"66","totalSnow_cm":"0.0","sunHour":"11.6","uvIndex":"11","hourly":[{"time":"0","tempC":"22","tempF":"71","windspeedMiles":"11","windspeedKmph":"17","winddirDegree":"304","winddir16Point":"NW","weatherCode":"176","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0025_light_rain_showers_night.png"}],"weatherDesc":[{"value":"Patchy rain possible"}],"precipMM":"0.1","humidity":"67","visibility":"10","pressure":"1018","cloudcover":"72","HeatIndexC":"23","HeatIndexF":"74","DewPointC":"15","DewPointF":"60","WindChillC":"22","WindChillF":"71","WindGustMiles":"19","WindGustKmph":"31","FeelsLikeC":"22","FeelsLikeF":"71","chanceofrain":"77","chanceofremdry":"0","chanceofwindy":"0","chanceofovercast":"92","chanceofsunshine":"0","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"300","tempC":"20","tempF":"67","windspeedMiles":"13","windspeedKmph":"20","winddirDegree":"323","winddir16Point":"NW","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0008_clear_sky_night.png"}],"weatherDesc":[{"value":"Clear"}],"precipMM":"0.1","humidity":"59","visibility":"10","pressure":"1017","cloudcover":"49","HeatIndexC":"20","HeatIndexF":"67","DewPointC":"12","DewPointF":"53","WindChillC":"20","WindChillF":"67","WindGustMiles":"22","WindGustKmph":"36","FeelsLikeC":"20","FeelsLikeF":"67","chanceofrain":"51","chanceofremdry":"30","chanceofwindy":"0","chanceofovercast":"63","chanceofsunshine":"29","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"600","tempC":"19","tempF":"67","windspeedMiles":"15","windspeedKmph":"24","winddirDegree":"340","winddir16Point":"NNW","weatherCode":"116","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0004_black_low_cloud.png"}],"weatherDesc":[{"value":"Partly cloudy"}],"precipMM":"0.0","humidity":"54","visibility":"10","pressure":"1018","cloudcover":"1","HeatIndexC":"19","HeatIndexF":"67","DewPointC":"10","DewPointF":"49","WindChillC":"19","WindChillF":"67","WindGustMiles":"24","WindGustKmph":"39","FeelsLikeC":"19","FeelsLikeF":"67","chanceofrain":"0","chanceofremdry":"88","chanceofwindy":"0","chanceofovercast":"13","chanceofsunshine":"83","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"900","tempC":"20","tempF":"68","windspeedMiles":"14","windspeedKmph":"22","winddirDegree":"339","winddir16Point":"NNW","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0001_sunny.png"}],"weatherDesc":[{"value":"Sunny"}],"precipMM":"0.0","humidity":"51","visibility":"10","pressure":"1020","cloudcover":"1","HeatIndexC":"21","HeatIndexF":"69","DewPointC":"9","DewPointF":"49","WindChillC":"20","WindChillF":"68","WindGustMiles":"19","WindGustKmph":"31","FeelsLikeC":"20","FeelsLikeF":"68","chanceofrain":"0","chanceofremdry":"87","chanceofwindy":"0","chanceofovercast":"26","chanceofsunshine":"80","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"11"},{"time":"1200","tempC":"23","tempF":"73","windspeedMiles":"13","windspeedKmph":"21","winddirDegree":"230","winddir16Point":"SW","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0001_sunny.png"}],"weatherDesc":[{"value":"Sunny"}],"precipMM":"0.0","humidity":"43","visibility":"10","pressure":"1020","cloudcover":"0","HeatIndexC":"24","HeatIndexF":"76","DewPointC":"9","DewPointF":"49","WindChillC":"23","WindChillF":"73","WindGustMiles":"15","WindGustKmph":"24","FeelsLikeC":"24","FeelsLikeF":"76","chanceofrain":"0","chanceofremdry":"88","chanceofwindy":"0","chanceofovercast":"0","chanceofsunshine":"88","chanceoffrost":"0","chanceofhightemp":"5","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"11"},{"time":"1500","tempC":"24","tempF":"74","windspeedMiles":"12","windspeedKmph":"20","winddirDegree":"12","winddir16Point":"NNE","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0001_sunny.png"}],"weatherDesc":[{"value":"Sunny"}],"precipMM":"0.0","humidity":"41","visibility":"10","pressure":"1019","cloudcover":"0","HeatIndexC":"25","HeatIndexF":"76","DewPointC":"10","DewPointF":"49","WindChillC":"24","WindChillF":"74","WindGustMiles":"14","WindGustKmph":"23","FeelsLikeC":"25","FeelsLikeF":"76","chanceofrain":"0","chanceofremdry":"83","chanceofwindy":"0","chanceofovercast":"0","chanceofsunshine":"86","chanceoffrost":"0","chanceofhightemp":"11","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"11"},{"time":"1800","tempC":"22","tempF":"71","windspeedMiles":"13","windspeedKmph":"21","winddirDegree":"33","winddir16Point":"NNE","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0001_sunny.png"}],"weatherDesc":[{"value":"Sunny"}],"precipMM":"0.0","humidity":"53","visibility":"10","pressure":"1019","cloudcover":"0","HeatIndexC":"23","HeatIndexF":"74","DewPointC":"12","DewPointF":"53","WindChillC":"22","WindChillF":"71","WindGustMiles":"16","WindGustKmph":"26","FeelsLikeC":"22","FeelsLikeF":"71","chanceofrain":"0","chanceofremdry":"86","chanceofwindy":"0","chanceofovercast":"0","chanceofsunshine":"86","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"11"},{"time":"2100","tempC":"20","tempF":"68","windspeedMiles":"11","windspeedKmph":"17","winddirDegree":"38","winddir16Point":"NE","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0008_clear_sky_night.png"}],"weatherDesc":[{"value":"Clear"}],"precipMM":"0.0","humidity":"60","visibility":"10","pressure":"1020","cloudcover":"0","HeatIndexC":"20","HeatIndexF":"68","DewPointC":"12","DewPointF":"54","WindChillC":"20","WindChillF":"68","WindGustMiles":"18","WindGustKmph":"29","FeelsLikeC":"20","FeelsLikeF":"68","chanceofrain":"0","chanceofremdry":"81","chanceofwindy":"0","chanceofovercast":"0","chanceofsunshine":"88","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"}]},{"date":"2020-04-28","astronomy":[{"sunrise":"06:44 AM","sunset":"07:54 PM","moonrise":"10:50 AM","moonset":"12:13 AM","moon_phase":"Waxing Crescent","moon_illumination":"37"}],"maxtempC":"25","maxtempF":"76","mintempC":"19","mintempF":"67","totalSnow_cm":"0.0","sunHour":"10.3","uvIndex":"9","hourly":[{"time":"0","tempC":"20","tempF":"68","windspeedMiles":"10","windspeedKmph":"16","winddirDegree":"34","winddir16Point":"NNE","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0008_clear_sky_night.png"}],"weatherDesc":[{"value":"Clear"}],"precipMM":"0.0","humidity":"61","visibility":"10","pressure":"1021","cloudcover":"0","HeatIndexC":"20","HeatIndexF":"68","DewPointC":"12","DewPointF":"54","WindChillC":"20","WindChillF":"68","WindGustMiles":"18","WindGustKmph":"28","FeelsLikeC":"20","FeelsLikeF":"68","chanceofrain":"0","chanceofremdry":"82","chanceofwindy":"0","chanceofovercast":"0","chanceofsunshine":"89","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"300","tempC":"20","tempF":"67","windspeedMiles":"9","windspeedKmph":"15","winddirDegree":"35","winddir16Point":"NE","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0008_clear_sky_night.png"}],"weatherDesc":[{"value":"Clear"}],"precipMM":"0.0","humidity":"61","visibility":"10","pressure":"1020","cloudcover":"0","HeatIndexC":"20","HeatIndexF":"67","DewPointC":"12","DewPointF":"53","WindChillC":"20","WindChillF":"67","WindGustMiles":"17","WindGustKmph":"27","FeelsLikeC":"20","FeelsLikeF":"67","chanceofrain":"0","chanceofremdry":"85","chanceofwindy":"0","chanceofovercast":"0","chanceofsunshine":"89","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"600","tempC":"20","tempF":"68","windspeedMiles":"9","windspeedKmph":"14","winddirDegree":"53","winddir16Point":"NE","weatherCode":"116","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0004_black_low_cloud.png"}],"weatherDesc":[{"value":"Partly cloudy"}],"precipMM":"0.0","humidity":"62","visibility":"10","pressure":"1020","cloudcover":"5","HeatIndexC":"20","HeatIndexF":"68","DewPointC":"13","DewPointF":"55","WindChillC":"20","WindChillF":"68","WindGustMiles":"15","WindGustKmph":"25","FeelsLikeC":"20","FeelsLikeF":"68","chanceofrain":"0","chanceofremdry":"86","chanceofwindy":"0","chanceofovercast":"13","chanceofsunshine":"84","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"},{"time":"900","tempC":"22","tempF":"71","windspeedMiles":"10","windspeedKmph":"16","winddirDegree":"69","winddir16Point":"ENE","weatherCode":"116","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0002_sunny_intervals.png"}],"weatherDesc":[{"value":"Partly cloudy"}],"precipMM":"0.0","humidity":"60","visibility":"10","pressure":"1021","cloudcover":"15","HeatIndexC":"22","HeatIndexF":"72","DewPointC":"14","DewPointF":"57","WindChillC":"22","WindChillF":"71","WindGustMiles":"14","WindGustKmph":"22","FeelsLikeC":"22","FeelsLikeF":"71","chanceofrain":"0","chanceofremdry":"87","chanceofwindy":"0","chanceofovercast":"43","chanceofsunshine":"74","chanceoffrost":"0","chanceofhightemp":"3","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"9"},{"time":"1200","tempC":"24","tempF":"75","windspeedMiles":"12","windspeedKmph":"19","winddirDegree":"77","winddir16Point":"ENE","weatherCode":"119","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0003_white_cloud.png"}],"weatherDesc":[{"value":"Cloudy"}],"precipMM":"0.0","humidity":"53","visibility":"10","pressure":"1022","cloudcover":"37","HeatIndexC":"25","HeatIndexF":"78","DewPointC":"14","DewPointF":"57","WindChillC":"24","WindChillF":"75","WindGustMiles":"14","WindGustKmph":"22","FeelsLikeC":"25","FeelsLikeF":"78","chanceofrain":"0","chanceofremdry":"86","chanceofwindy":"0","chanceofovercast":"63","chanceofsunshine":"56","chanceoffrost":"0","chanceofhightemp":"12","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"9"},{"time":"1500","tempC":"25","tempF":"76","windspeedMiles":"11","windspeedKmph":"18","winddirDegree":"83","winddir16Point":"E","weatherCode":"116","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0002_sunny_intervals.png"}],"weatherDesc":[{"value":"Partly cloudy"}],"precipMM":"0.0","humidity":"52","visibility":"10","pressure":"1020","cloudcover":"65","HeatIndexC":"26","HeatIndexF":"78","DewPointC":"14","DewPointF":"57","WindChillC":"25","WindChillF":"76","WindGustMiles":"13","WindGustKmph":"21","FeelsLikeC":"26","FeelsLikeF":"78","chanceofrain":"0","chanceofremdry":"86","chanceofwindy":"0","chanceofovercast":"72","chanceofsunshine":"35","chanceoffrost":"0","chanceofhightemp":"15","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"9"},{"time":"1800","tempC":"23","tempF":"74","windspeedMiles":"9","windspeedKmph":"15","winddirDegree":"88","winddir16Point":"E","weatherCode":"116","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0002_sunny_intervals.png"}],"weatherDesc":[{"value":"Partly cloudy"}],"precipMM":"0.0","humidity":"56","visibility":"10","pressure":"1019","cloudcover":"16","HeatIndexC":"25","HeatIndexF":"77","DewPointC":"14","DewPointF":"57","WindChillC":"23","WindChillF":"74","WindGustMiles":"13","WindGustKmph":"20","FeelsLikeC":"25","FeelsLikeF":"77","chanceofrain":"0","chanceofremdry":"91","chanceofwindy":"0","chanceofovercast":"35","chanceofsunshine":"82","chanceoffrost":"0","chanceofhightemp":"10","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"9"},{"time":"2100","tempC":"22","tempF":"71","windspeedMiles":"8","windspeedKmph":"13","winddirDegree":"90","winddir16Point":"E","weatherCode":"113","weatherIconUrl":[{"value":"http://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0008_clear_sky_night.png"}],"weatherDesc":[{"value":"Clear"}],"precipMM":"0.0","humidity":"61","visibility":"10","pressure":"1020","cloudcover":"6","HeatIndexC":"25","HeatIndexF":"76","DewPointC":"14","DewPointF":"57","WindChillC":"22","WindChillF":"71","WindGustMiles":"14","WindGustKmph":"23","FeelsLikeC":"22","FeelsLikeF":"71","chanceofrain":"0","chanceofremdry":"89","chanceofwindy":"0","chanceofovercast":"28","chanceofsunshine":"84","chanceoffrost":"0","chanceofhightemp":"0","chanceoffog":"0","chanceofsnow":"0","chanceofthunder":"0","uvIndex":"1"}]}]}} ================================================ FILE: test/proxy-data/data1.headers ================================================ {"Content-Type": "application/json"} ================================================ FILE: test/query.sh ================================================ queries=( / /Kiev /Kiev.png /?T /Киев /Kiev?2 "/Kiev?format=1" "/Kiev?format=2" "/Kiev?format=3" "/Kiev?format=4" "/Kiev?format=v2" "/Kiev?format=%s" "/Kiev?format=%S" "/Kiev?format=%D+%S+%z+%s+%d" "/:help" "/Kiev?T" "/Kiev?p" "/Kiev?q" "/Kiev?Q" "/Kiev_text=no_view=v2.png" "/Kiev.png?1nqF" "/Kiev_1nqF.png" ) options=$(cat < test-data/signatures fi result_tmp=$(mktemp wttrin-test-XXXXX) while read -r -a args do for q in "${queries[@]}"; do signature=$(echo "${args[@]}" "$q" | sha1sum | awk '{print $1}') curl -ks "${args[@]}" "$server$q" > "$result_tmp" result=$(sha1sum "$result_tmp" | awk '{print $1}') # this must be moved to the server # but for the moment we just clean up # the cache after each call rm -rf "/wttr.in/cache" if grep -Eq "(we are running out of queries|500 Internal Server Error)" "$result_tmp"; then echo "$q" fi if [[ $UPDATE = yes ]]; then printf "%s %s %s\\n" "$signature" "$result" "${args[*]} $q" >> test-data/signatures elif ! grep -q "$signature $result" test-data/signatures; then echo "FAILED: curl -ks ${args[*]} $server$q" fi done done <<< "${options}" rm "$result_tmp" ================================================ FILE: test/test-data/signatures ================================================ 8f27084b6294ddbe28dbcbf98f798730e8a79289 4ee2dd9cf8f5818902647ff832ef40d690096bf1 / ae537911bb7b0568f478073e661abee1cb4ff941 d123e570da22dee9798d353c4281cb5a2bdbaeac /Kiev 4dc586807c16020b9f4dbb705326c698bea41665 a186d89e95061a7887c005ffa8bd1e29362de2da /Kiev.png 3db1938bedc0ee0047bf3b043ddaf0aba1912f13 febab92af9526163bc9e502ecd7fa4225345e6f6 /?T 2cc0ba7a57a6342e72fd7142ca18dbb0eae69416 ce7fb7a88cab697f5280ddabf344f0d397888956 /Киев 928142e88da142ea8075cbfe09bfef349e72dbb1 0f86f59a45b4485fea1375ca945503d9abb9a96d /Kiev?2 4f6f0a16ff415fad1c102c8023c5d8365ef63402 de3b9821d587753149eded5411ec397e7a2000e2 /Kiev?format=1 c99903b86971ccccfcca4f13e6fca72776b4fbcf f64086e48d84ac6eb440ca080eff28de1470ec30 /Kiev?format=2 2a0d6cd8d30a84328580611ca6dd6bed1d805a04 da7b79cdff330edc1a7a78fcf2f9c8bf7e432d40 /Kiev?format=3 4e4e15eeb6a8b6ddb5d00591c4b5e9b74a13e6bc 6a61305ea631fbccb98090c62a96898e8ffa0d75 /Kiev?format=4 a27d3e4ad7f820124ef57c9299715bc61cb71387 fed12e63dd5fb5e348ee30d94c7231112eca71cd /Kiev?format=v2 cbe9fc56091b519e6aebcedd9a7541241f4c4cda 2d97c405f1557b822cc86b038aeea40c3eb79d7d /Kiev?format=%s 84b79ec29670254c3570901e4c5db017516e088d 3f26bdbbd5faf7729f87a9cb964d407493e9a0d6 /Kiev?format=%S 8a0111eb7a519adad1210661bbb49f960ba7f95f fe58cbd420cf36a910551a1037f5c4fa19b31074 /Kiev?format=%D+%S+%z+%s+%d 83cc0ef08c24ad7ecc81d1a6cbd693bb06214ece 0ea998c1b53e452a373699ab953ab00e8a2870f0 /:help 310b64f65fc9f66a5142bf6104f4f9b9d5eef0ea b0bd07f0c87aae9464c091ccb955f41ec6973098 /Kiev?T 9bd1b460d4927df24724f45f69bd3132f3de8e04 d001bf6ab36b6c14f98f02fc4500706d7a9f05a8 /Kiev?p 3ee1a25d436799804d7ebd8371d8022fa55a71d7 80f18be012d0471dce9fcd2b500f482bcd635347 /Kiev?q e0e8e7eca16bfac88503ac6d19a7a6c8b469c0fd d5d070c98237f0dffc82b176039f90a15f03a667 /Kiev?Q d08d1fa2546fee0717d1eb663cf63cd1505b8885 e380ef1a22f62a7fa1133d0e35d923c8587cb3ed /Kiev_text=no_view=v2.png b14e89e2c183139495bad5404748b3b6173063d7 ff70b13244929fa3a934e94f99f019c66501ad43 /Kiev.png?1nqF 04e1a945bdde39d646e397e55c096ec46b53a92b ff70b13244929fa3a934e94f99f019c66501ad43 /Kiev_1nqF.png 3e1be80e942a2ea5450c60e1c0ebfb154aca3da1 6a5bdefe64689f4d05128bd62a8118f4f2f52043 -A firefox / ee6bf0665c2719cda3ec1fbdb80413d821c99b8e 3f9c5091269ece259cce13fc842265019001ed54 -A firefox /Kiev 98ef11678b7fd33425f97eeee70e00cd96206539 a186d89e95061a7887c005ffa8bd1e29362de2da -A firefox /Kiev.png ecbcf2cb9004a754c4559ce7e92fead68f71721a 2ea6d52a2108a481cbc0f44a881eb88642d68e80 -A firefox /?T 74206d869128383dba2d840b848b90eb376fd851 7c6ce53ff25d91a5f46baa30077b69e0f09f2571 -A firefox /Киев 91b89025b5acd56ca475924e0eb559a9734f3333 dbd49d93eff2b2cf82f7d266f90de950207a0561 -A firefox /Kiev?2 e6cb82dab95e05167ffbcb90a10d6cb03cd02ec1 de3b9821d587753149eded5411ec397e7a2000e2 -A firefox /Kiev?format=1 e65bc57e8d1df26c442a9ecf45afee390ff331a3 f64086e48d84ac6eb440ca080eff28de1470ec30 -A firefox /Kiev?format=2 d743b331d5f4c81bbc8b168ce84a99ab22dc70cf da7b79cdff330edc1a7a78fcf2f9c8bf7e432d40 -A firefox /Kiev?format=3 bf359ee92690c3a3061542dc6e78cb42ca837412 6a61305ea631fbccb98090c62a96898e8ffa0d75 -A firefox /Kiev?format=4 cb875772a6610c991b95b3fbfa22fc7192e25843 5367fd6790d55639b1536ec71abf340e5c79ff45 -A firefox /Kiev?format=v2 91c0076d8e6665c06aab7c7b2326b29718bfeb80 2d97c405f1557b822cc86b038aeea40c3eb79d7d -A firefox /Kiev?format=%s ae9d7b1ee27eb8201a0726a3e24fe195cf2ae9e4 3f26bdbbd5faf7729f87a9cb964d407493e9a0d6 -A firefox /Kiev?format=%S e94371697b70bf956b6f9352fad913d716e774e7 fe58cbd420cf36a910551a1037f5c4fa19b31074 -A firefox /Kiev?format=%D+%S+%z+%s+%d d520af45b491689d53024c696955db8b1e4eaa87 e916b140b1297cf5bea16d92a91260e9dc3e2bc9 -A firefox /:help 6b80492b79a4cc510cb4a9654cb6ff085cdc1943 801d4c6d6837c9604944168a3930dfe05b0e9f5d -A firefox /Kiev?T e13d7449ce756e55ba3e84e4b7e601b36d0044b6 c5a87804710ab70b8798f56579d276f4ce1806bf -A firefox /Kiev?p 3f6c192a6da5b79ea59ef94e99b9cbf4b0e7ede2 372aca50f441920ad623d62ee8fcde46d609f6f9 -A firefox /Kiev?q f28ca2a7a47f4859eac7d0307ec7ac67a40e0adf 1b4b66d58bd7e27abeeca45581e686f12fefe76a -A firefox /Kiev?Q 62c5029cf297b1434c57228dc8c8cdfb5e68285d e380ef1a22f62a7fa1133d0e35d923c8587cb3ed -A firefox /Kiev_text=no_view=v2.png 22aa5dc06076b086de776a3c601544b250fef07c ff70b13244929fa3a934e94f99f019c66501ad43 -A firefox /Kiev.png?1nqF 3a00ecab00da83f70c2abf30379c5e79c791e383 ff70b13244929fa3a934e94f99f019c66501ad43 -A firefox /Kiev_1nqF.png ed573b89ca5522d6ab69dc1686b98b00391076bd 5ce7ea58bf02bff008baa3193b2db498268a244b -H Accept-Language:ru / b879673f66235bbf1913ff9abc58aff2fb8962d1 00a96a5d83608c2dad7921862bb3f244775f6b19 -H Accept-Language:ru /Kiev 83d99896cf866ecbaa6d2c64c12bd31bc7b35068 92dc07acb93633974eaff19e8c1a99e590e140d9 -H Accept-Language:ru /Kiev.png 9cbb6aa3e0b46e78229a32688db1cced9a44271d b368cc8f39e7a7ced04e3f4e6506e1eb4551e904 -H Accept-Language:ru /?T 095d8d38c667923131801595b903e007b5f902f3 4ede3397f9def696adc7ecf3ffd46a59b8fb25cb -H Accept-Language:ru /Киев 4e6cdfc38c9d9f2436438b345776c42cb8cab8a5 1b00c96a05f9daea8248a8e063d990797be933ad -H Accept-Language:ru /Kiev?2 b7d8d0f0bb4c38aabac468c9a354bb4e2b401893 de3b9821d587753149eded5411ec397e7a2000e2 -H Accept-Language:ru /Kiev?format=1 8f3bbfc9be6418e82edacecc54a9f3e9f26b7fbf f64086e48d84ac6eb440ca080eff28de1470ec30 -H Accept-Language:ru /Kiev?format=2 f1d4178892fd3dc38e9f966112d317859acc9122 da7b79cdff330edc1a7a78fcf2f9c8bf7e432d40 -H Accept-Language:ru /Kiev?format=3 cf44e154504d9bc2b9b6066bbb0f5d52fc12f13e 6a61305ea631fbccb98090c62a96898e8ffa0d75 -H Accept-Language:ru /Kiev?format=4 4955c849f67da53203b8c96b15a0bf0a4a471bc6 e2d3509ef7f6c3fc84151f55e1c5eb2f28dfd155 -H Accept-Language:ru /Kiev?format=v2 e23e33569bbe34de944dad3a647d2a7a525513b4 2d97c405f1557b822cc86b038aeea40c3eb79d7d -H Accept-Language:ru /Kiev?format=%s d3c44cb57a1ba487b9fe7ec37368d00eee5b4601 3f26bdbbd5faf7729f87a9cb964d407493e9a0d6 -H Accept-Language:ru /Kiev?format=%S db91cc89883050beedd2afac7c74276a4d2dcf42 fe58cbd420cf36a910551a1037f5c4fa19b31074 -H Accept-Language:ru /Kiev?format=%D+%S+%z+%s+%d 3f69f4a605ce88643b4e0d62a588c92625d41aea 73b4142cd3af43472897989c61408d5765c2a6ef -H Accept-Language:ru /:help 08553ca4bf71c738c4321fe7d84b4e6ff830956f 016fc03b18a8902f838719bbc171184603c08b60 -H Accept-Language:ru /Kiev?T b70f8b3fc8aee126c04b27b0d3b4c503b4292cbf b60b68a9e77275884812f7e52b06f6012ba5682a -H Accept-Language:ru /Kiev?p 400efdba61125f8cb850d7c33caf4fc2739a960b 5ee4a043a91509ef57aec46a14a0c24f09e8ec47 -H Accept-Language:ru /Kiev?q b9fd454e73343f262a6d99dd80487495bd647c6f a10718896a07baadb87adf2bf0026b1f00252213 -H Accept-Language:ru /Kiev?Q 8fed034e57624d0e0b33140673094e56e04087bc 83d236565782aff7416bf526b38636148d6ba15a -H Accept-Language:ru /Kiev_text=no_view=v2.png cf618118ddeff08355620e83a693c40239990545 37a4dd271b5a66513da4a6dc9caeab0ae15814db -H Accept-Language:ru /Kiev.png?1nqF 0f106ea5605ecc852ab6f63eee348a9a954e137f 37a4dd271b5a66513da4a6dc9caeab0ae15814db -H Accept-Language:ru /Kiev_1nqF.png 3ce3dd46413f236244410f142a4b44356a0cedf9 1c2eea391b35c8bfed2541435e1788307aa06bc1 -H X-Forwarded-For:1.1.1.1 / 89be0a5787592298ce34f10b36da7ee87d1a1353 d123e570da22dee9798d353c4281cb5a2bdbaeac -H X-Forwarded-For:1.1.1.1 /Kiev a9977eadc628b1ede5d4f91ee103dfb740caa2b1 a186d89e95061a7887c005ffa8bd1e29362de2da -H X-Forwarded-For:1.1.1.1 /Kiev.png eec20c6be5e528967cddf6d0b72c84dbda553d43 9a39dbafa7e1550d374e38059c0f4b8f437e1739 -H X-Forwarded-For:1.1.1.1 /?T e304153f0e1e9b41781bf4eb6fb6c4a5b7513aec ce7fb7a88cab697f5280ddabf344f0d397888956 -H X-Forwarded-For:1.1.1.1 /Киев 98f0b3a28863a861c6ac6d89ee5d49adb7f3f518 0f86f59a45b4485fea1375ca945503d9abb9a96d -H X-Forwarded-For:1.1.1.1 /Kiev?2 cf04d7fe2cf36eba8d7fb4fa6def1c9015036456 de3b9821d587753149eded5411ec397e7a2000e2 -H X-Forwarded-For:1.1.1.1 /Kiev?format=1 67fbe9168566709450eb35d36c60c27105335a7e f64086e48d84ac6eb440ca080eff28de1470ec30 -H X-Forwarded-For:1.1.1.1 /Kiev?format=2 b2604348bf39774c85b7c18ae7b51f63a2c9f31a da7b79cdff330edc1a7a78fcf2f9c8bf7e432d40 -H X-Forwarded-For:1.1.1.1 /Kiev?format=3 cf012400156c842e569b6a9f05b094e6b75348cd 6a61305ea631fbccb98090c62a96898e8ffa0d75 -H X-Forwarded-For:1.1.1.1 /Kiev?format=4 1f4981348cab19df9846cd3b3923ee7a972ff9fa fed12e63dd5fb5e348ee30d94c7231112eca71cd -H X-Forwarded-For:1.1.1.1 /Kiev?format=v2 e7d772042819bb62e6e259656a75bd7b0621d1da 2d97c405f1557b822cc86b038aeea40c3eb79d7d -H X-Forwarded-For:1.1.1.1 /Kiev?format=%s 7db5a1daac653383c18aec25ad8583f2e5296845 3f26bdbbd5faf7729f87a9cb964d407493e9a0d6 -H X-Forwarded-For:1.1.1.1 /Kiev?format=%S a9331379fa4b5d61b5a87a8e4cd4412cdae970a1 fe58cbd420cf36a910551a1037f5c4fa19b31074 -H X-Forwarded-For:1.1.1.1 /Kiev?format=%D+%S+%z+%s+%d 767a7407c14049fd77a6a2fedd1d8b35f6e47e0d 0ea998c1b53e452a373699ab953ab00e8a2870f0 -H X-Forwarded-For:1.1.1.1 /:help 10631d55b42e7bc5ec15ffc5cddae712785eb354 b0bd07f0c87aae9464c091ccb955f41ec6973098 -H X-Forwarded-For:1.1.1.1 /Kiev?T 031478f562663eb9f577b04032993e2f098146f6 d001bf6ab36b6c14f98f02fc4500706d7a9f05a8 -H X-Forwarded-For:1.1.1.1 /Kiev?p e106cd21a6b67196159c2baa023142e3a8859612 80f18be012d0471dce9fcd2b500f482bcd635347 -H X-Forwarded-For:1.1.1.1 /Kiev?q 1a16c9b52ba90cb7ad3dd8902bf41b31a287d49e d5d070c98237f0dffc82b176039f90a15f03a667 -H X-Forwarded-For:1.1.1.1 /Kiev?Q 83bd9cc6a646e44b75524474dd32f0fd1f5c5a39 e380ef1a22f62a7fa1133d0e35d923c8587cb3ed -H X-Forwarded-For:1.1.1.1 /Kiev_text=no_view=v2.png ac8dabb4d30cf6a6fae626e6fc29a4f2d9df0164 ff70b13244929fa3a934e94f99f019c66501ad43 -H X-Forwarded-For:1.1.1.1 /Kiev.png?1nqF f0b3c0851a03a9b182ab938fc7f14935bf7af1f0 ff70b13244929fa3a934e94f99f019c66501ad43 -H X-Forwarded-For:1.1.1.1 /Kiev_1nqF.png