Skip to content

Commit

Permalink
Add Travis CI
Browse files Browse the repository at this point in the history
  • Loading branch information
vyahello committed Jan 4, 2020
1 parent e43074e commit 4f9c00e
Show file tree
Hide file tree
Showing 39 changed files with 232 additions and 111 deletions.
5 changes: 5 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[flake8]
max_complexity = 10
max-line-length = 120
statistics = True
count = True
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ venv
# python identifiers
.python-version
.pytest_cache/
.coverage
test-report.html
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: python
python:
- "3.6"
- "3.7"
- "3.8"
before_install:
- pip install pip -U
- pip install -r requirements-dev.txt -U
script:
- ./analyse-code.sh
after_success:
- coveralls
notifications:
email: false
50 changes: 39 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,61 @@
# Weather telegram bot
> Basic telegram bot powered by a webhook that helps you to get current weather in a specific city all around the world.
[![Build Status](https://travis-ci.org/vyahello/weather-chatbot.svg?branch=master)](https://travis-ci.org/vyahello/weather-chatbot)
[![Coverage Status](https://coveralls.io/repos/github/vyahello/weather-chatbot/badge.svg?branch=master)](https://coveralls.io/github/vyahello/weather-chatbot?branch=master)
[![GitHub version](https://badge.fury.io/gh/vyahello%2Fweather-chatbot.svg)](https://github.com/vyahello/weather-chatbot/releases)
[![GitHub watchers](https://img.shields.io/github/watchers/vyahello/weather-chatbot.svg)](https://GitHub.com/vyahello/weather-chatbot/graphs/watchers/)
[![Forks](https://img.shields.io/github/forks/vyahello/weather-chatbot)](https://github.com/vyahello/weather-chatbot/network/members)
[![Stars](https://img.shields.io/github/stars/vyahello/weather-chatbot)](https://github.com/vyahello/weather-chatbot/stargazers)
[![GitHub contributors](https://img.shields.io/github/contributors/vyahello/weather-chatbot.svg)](https://GitHub.com/vyahello/weather-chatbot/graphs/contributors/)

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE.md)
[![Hits-of-Code](https://hitsofcode.com/github/vyahello/weather-chatbot)](https://hitsofcode.com/view/github/vyahello/weather-chatbot)

# Weather chatbot
> Simple telegram bot that helps you to get current weather in a specific city all around the world.
>
> Bot is called `WeatherBot` that served by [pythonanywhere.com](https://pythonanywhere.com) hosting, search for it in the > `telegram` app to allow it help you to get your desired weather value. Enjoy it!
> Bot is called `WeatherBot` that served by, search for it in the `telegram` app to allow it help you to get your desired weather value.
> Enjoy it!
**Tools**
- python 3.6+
- [flask](https://pypi.org/project/Flask/)
- [pytest](https://pypi.org/project/pytest/)
- [travis CI](https://travis-ci.org)
- [pythonanywhere](https://pythonanywhere.com)

## Usage
Run script from the root directory of the project:
```bash
~ python bot.py
~ python chat.py
```

## Demo
![Screenshot](bin/demo/bot.png)
![Screenshot](src/demo/bot.png)

## Run tests
## Development notes

### Run unittests
Please run tests from the root directory of the project:
```bash
~ pytest -v
~ pytest
```

## Meta
Author – Volodymyr Yahello vyahello@gmail.com
### Release notes

* 0.1.0
* Add Travis CI

### Meta
Author – Volodymyr Yahello

Distributed under the `MIT` license. See [LICENSE](LICENSE.md) for more information.
Distributed under the `Apache 2.0` license. See [LICENSE](LICENSE.md) for more information.

You can reach out me at:
* [vyahello@gmail.com](vyahello@gmail.com)
* [https://github.com/vyahello](https://github.com/vyahello)
* [https://www.linkedin.com/in/volodymyr-yahello-821746127](https://www.linkedin.com/in/volodymyr-yahello-821746127)

## Contributing
### Contributing
1. clone the repository
2. configure Git for the first time after cloning with your `name` and `email`
3. `pip install -r requirements.txt` to install all project dependencies
3. `pip install -r requirements-dev.txt` to install all project development dependencies
26 changes: 26 additions & 0 deletions analyse-code.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash

PACKAGE="src"


check-black() {
printf "Start black analysis ..." && ( black --check ${PACKAGE} )
}


check-flake() {
printf "Start flake8 analysis ..." && ( flake8 ${PACKAGE} )
}


check-unittests() {
printf "Start unittests analysis ..." && pytest
}


main() {
check-black && check-flake && check-unittests
}


main
1 change: 0 additions & 1 deletion bin/bot/__init__.py

This file was deleted.

1 change: 0 additions & 1 deletion bin/forecast/__init__.py

This file was deleted.

10 changes: 0 additions & 10 deletions bin/server/__init__.py

This file was deleted.

19 changes: 0 additions & 19 deletions bin/server/routes.py

This file was deleted.

6 changes: 0 additions & 6 deletions bot.py

This file was deleted.

9 changes: 9 additions & 0 deletions chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from src.server import server


def run_weather_chatbot() -> None:
server.run()


if __name__ == "__main__":
run_weather_chatbot()
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[tool.black]
line-length = 120
target-version = ["py36", "py37", "py38"]
exclude = '''
/(
\.pytest_cache
)/
'''
9 changes: 9 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[pytest]
python_files=*.py
python_functions=test_*
addopts = -rsxX
-q
-v
--self-contained-html
--html=test-report.html
--cov=src
9 changes: 9 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
black==19.10b0
flake8==3.7.9
pytest==5.2.2
pytest-html==2.0.1
pytest-cov==2.8.1
pytest-clarity==0.2.0a1
coverage==4.5.4
coveralls==1.8.2
pdbpp==0.10.2
9 changes: 4 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
werkzeug
requests
pytest
flask
flask_sslify
werkzeug==0.16.0
requests==2.22.0
flask==1.1.1
flask_sslify==0.1.5
File renamed without changes.
1 change: 1 addition & 0 deletions src/bot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BOT_API_TOKEN: str = "523806969:AAGmLcMpcH_wUd69KB6JeN3ETGUtXbjoGz0"
31 changes: 17 additions & 14 deletions bin/bot/messages.py → src/bot/messages.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from abc import ABC, abstractmethod
from typing import Dict, Any, Callable
from bin.bot import BOT_API_TOKEN
from bin.forecast.city import CityWeatherSummary
from bin.forecast.weather import OpenWeatherMap
from bin.server import requests
from bin.web_api.requests import Request, SafeBotRequest
from bin.web_api.responses import Response
from bin.web_api.urls import CommonUrl
from src.bot import BOT_API_TOKEN
from src.forecast.city import CityWeatherSummary
from src.forecast.weather import OpenWeatherMap
from src.server import requests
from src.web.requests import Request, SafeBotRequest
from src.web.responses import Response
from src.web.urls import CommonUrl


class Answer(ABC):
Expand Down Expand Up @@ -34,26 +34,29 @@ class BotAnswer(Answer):

def __init__(self, request: requests.Request) -> None:
def _req() -> Dict[Any, Any]:
return request.dct().get('message')
return request.dct().get("message")

self._req: Callable[..., Dict[Any, Any]] = _req

def chat_id(self) -> int:
return self._req().get('chat').get('id')
return self._req().get("chat").get("id")

def message(self) -> str:
return self._req().get('text')
return self._req().get("text")


class BotMessage(Message):
"""A message of a bot."""

def __init__(self, chat_id: int, city: str) -> None:
self._chat_id: int = chat_id
self._city_summary = lambda: CityWeatherSummary(OpenWeatherMap(city.lstrip('/')).data_records())
self._req: Request = SafeBotRequest(CommonUrl('https://api.telegram.org/bot', BOT_API_TOKEN, '/sendMessage'))
self._city_summary = lambda: CityWeatherSummary(OpenWeatherMap(city.lstrip("/")).data_records())
self._req: Request = SafeBotRequest(CommonUrl("https://api.telegram.org/bot", BOT_API_TOKEN, "/sendMessage"))

def send(self) -> Response:
return self._req.post(
{'chat_id': self._chat_id,
'text': "{name} {country}, {temperature}°C, {description}".format(**self._city_summary().get())})
{
"chat_id": self._chat_id,
"text": "{name} {country}, {temperature}°C, {description}".format(**self._city_summary().get()),
}
)
2 changes: 1 addition & 1 deletion bin/bot/text.py → src/bot/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def get(self) -> str:
class InputText(Text):
"""Parse input message."""

def __init__(self, text: str, pattern: str = r'\/[\w\s]+') -> None:
def __init__(self, text: str, pattern: str = r"\/[\w\s]+") -> None:
self._pattern: str = pattern
self._text: str = text

Expand Down
File renamed without changes
1 change: 1 addition & 0 deletions src/forecast/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WEATHER_ID: str = "9ee3204c7bf31f11025a12e826303b84"
12 changes: 6 additions & 6 deletions bin/forecast/city.py → src/forecast/city.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,16 @@ def __init__(self, weather_data: Dict[Any, Any]) -> None:
self._weather_data: Dict[Any, Any] = weather_data

def description(self) -> str:
return self._weather_data.get('weather')[0].get('description')
return self._weather_data.get("weather")[0].get("description")

def temp(self) -> float:
return self._weather_data.get('main').get('temp')
return self._weather_data.get("main").get("temp")

def country(self) -> str:
return self._weather_data.get('sys').get('country')
return self._weather_data.get("sys").get("country")

def name(self) -> str:
return self._weather_data.get('name')
return self._weather_data.get("name")


class CityWeatherSummary(Summary):
Expand All @@ -60,5 +60,5 @@ def get(self) -> Dict[str, Any]:
description=self._city_weather.description(),
temperature=self._city_weather.temp(),
country=self._city_weather.country(),
name=self._city_weather.name()
)
name=self._city_weather.name(),
)
9 changes: 5 additions & 4 deletions bin/forecast/weather.py → src/forecast/weather.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from abc import ABC, abstractmethod
from typing import Dict
from bin.forecast import WEATHER_ID
from bin.web_api.requests import Request, SafeBotRequest
from bin.web_api.urls import CommonUrl
from src.forecast import WEATHER_ID
from src.web.requests import Request, SafeBotRequest
from src.web.urls import CommonUrl


class Weather(ABC):
Expand All @@ -18,7 +18,8 @@ class OpenWeatherMap(Weather):

def __init__(self, city: str) -> None:
self._req: Request = SafeBotRequest(
CommonUrl('http://api.openweathermap.org/data/2.5/weather?q=', city, '&units=metric&appid=', WEATHER_ID))
CommonUrl("http://api.openweathermap.org/data/2.5/weather?q=", city, "&units=metric&appid=", WEATHER_ID)
)

def data_records(self) -> Dict[str, str]:
return self._req.get().json()
7 changes: 7 additions & 0 deletions src/server/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# flake8: noqa
from src.server.core import Server, WebServer
from src.server.requests import Request, ServerRequest

server: Server = WebServer()

from . import routes
9 changes: 8 additions & 1 deletion bin/server/core.py → src/server/core.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from abc import ABC, abstractmethod
from typing import Any, Callable, Iterable
from flask import Flask
from flask import Flask, render_template


class Server(ABC):
Expand All @@ -14,6 +14,10 @@ def route(self, path: str, methods: Iterable[str]) -> Callable[..., Any]:
def run(self, host: str = None, port: int = None, debug: Any = None, **options: Any) -> None:
pass

@abstractmethod
def render_template(self, template: str, **kwargs: Any) -> str:
pass


class WebServer(Server):
"""Represent web server."""
Expand All @@ -26,3 +30,6 @@ def route(self, path: str, methods: Iterable[str]) -> Callable[..., Any]:

def run(self, host: str = None, port: int = None, debug: Any = None, **options: Any) -> None:
return self._app.run(host, port, debug, **options)

def render_template(self, template: str, **kwargs: Any) -> str:
return render_template(template, **kwargs)
File renamed without changes.
17 changes: 17 additions & 0 deletions src/server/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from src.bot.messages import Answer, BotAnswer, BotMessage
from src.bot.text import Text, InputText
from src.server import server, Request, ServerRequest


@server.route("/", methods=("GET", "POST"))
def index():
request: Request = ServerRequest()
answer: Answer = BotAnswer(request)

if request.method() == "POST":
text: Text = InputText(answer.message())

if text.match():
BotMessage(answer.chat_id(), text.get()).send()

return server.render_template("index.html")

0 comments on commit 4f9c00e

Please sign in to comment.