Skip to content

Commit

Permalink
add travis config
Browse files Browse the repository at this point in the history
  • Loading branch information
vanyakosmos committed Aug 3, 2019
1 parent f39b71d commit 6aec910
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 53 deletions.
19 changes: 19 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[run]
omit =
manage.py
conftest.py
*/migrations/*
*/test*

[report]
exclude_lines =
pragma: no cover
def __str__
def __repr__
raise AssertionError
raise NotImplementedError
if __name__ == '__main__':

precision = 2
show_missing = True
sort = Miss
27 changes: 27 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
sudo: required
language: python

python: 3.6

services:
- postgresql

install:
- pip install -r requirements

script:
- pytest -vv --cov . --cov-report xml

after_success:
- codecov

deploy:
provider: heroku
api_key:
secure: RVFbkZ17sMcl0NmleKYEE1Sd8cMsP3OsH3n3FiPSh/ZAH+W8VnGw86auaHidn4qIc7jXFRQ64UmuFG1dWmQVPpj7wuNVBwginH1RQ4nb0TTIT5xbBIQjNTUnLAvmCNTnRRiuUuAfmC31OTVlN417eFmKA10kT93MqEkHsPonot0tAbcmGgsZgscwadxGJf/qnZaM8f3o440qaD8Jy4CGap28wkTr+V2wW8St6bZllBX4fGKrKstOquYSsBnnjTJJpBUHy5p7Ql88n+DWfZFIEFz1uejcVZ+LqHmJEsHfvUkskN2GBzWprO5GimnEhDHHp3T/iOF2OYEWoReWuDFCFkTWnFO58NaZnvl5JpDvIWZ0zduFMdZxhM5sJkMawDKvm03WDq6whILxEAo4EWuWm16JhE4cF8zmiCHkyiPYqJyM2+VEeZ1TFEeXEL7DP1KI5zesWPi+chz5AL8q4Q7ji3f58tzYWwTl0TC9d8lwTxrCs/FjZwIaBVUV+JeUaZWUhH7EcZYd2HmVYL1Z1XSEfTp4S4fQ5Q8P6qBUy1p07M4yG3oBkGjgAeHFpNip9cUCnUhfcnL87+GaufyB8pR0lRbBygJxmJf8asKOkaGYCqMk4iJvqTg+KRHyb3uqLL2Bj1QWOdirtkv100jowrwA7AjXoytGK2zyEJBxe/K7JPo=
app:
master: travis-tg

notifications:
webhooks:
- https://travis-tg.herokuapp.com/
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Travis CI telegram notifications

[![Build Status](https://travis-ci.org/vanyakosmos/travis-tg-notifier.svg?branch=master)](https://travis-ci.org/vanyakosmos/travis-tg-notifier)
[![Coverage](https://codecov.io/gh/vanyakosmos/travis-tg-notifier/branch/master/graph/badge.svg)](https://codecov.io/gh/vanyakosmos/travis-tg-notifier)
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/vanyakosmos/travis-tg-notifier/tree/master)

## Usage
Expand Down
4 changes: 4 additions & 0 deletions core/bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.conf import settings
from telegram import Bot

bot = Bot(settings.TELEGRAM_BOT_TOKEN)
32 changes: 32 additions & 0 deletions core/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import uuid
from typing import Callable

import pytest
from _pytest.fixtures import FixtureRequest
from django.contrib.auth import get_user_model

User = get_user_model()


def get_id():
# generate 128 bit number and shift it by 64
return str(uuid.uuid1().int >> 64)


def append_to_cls(request: FixtureRequest, func: Callable, name: str = None):
name = name or func.__name__.strip('_')
setattr(request.cls, name, staticmethod(func))
return func


@pytest.fixture(scope='class')
def create_user(request: FixtureRequest):
def _factory(**kwargs):
fields = {
'username': get_id(),
**kwargs,
}
user = User.objects.create_user(**fields)
return user

return append_to_cls(request, _factory, 'create_user')
40 changes: 38 additions & 2 deletions core/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
from django.test import TestCase
import pytest
from django.contrib.auth import get_user_model
from django.http import HttpRequest, HttpResponse

# Create your tests here.
from core.utils import render_index, get_message, get_user

User = get_user_model()


@pytest.mark.usefixtures('create_user')
@pytest.mark.django_db
class TestUtils:
def test_render_index(self):
request = HttpRequest()
res = render_index(request)
assert isinstance(res, HttpResponse)
assert 'usage' in res.content.decode().lower()

def test_get_message(self):
s = get_message({'a': '1', 'c': '2', 'b': '3'})
assert s == 'a=1\nb=3\nc=2'

def test_get_user_invalid(self):
with pytest.raises(KeyError):
get_user({})

def test_get_user_new(self):
assert User.objects.count() == 0
u = get_user({'id': '1234', 'first_name': 'name'})
assert User.objects.count() == 1
assert u.first_name == 'name'

def test_get_user(self):
user = self.create_user(first_name='a')
u = get_user({'id': user.username, 'first_name': 'b'})
assert u.id == user.id
user.refresh_from_db()
assert user.first_name == 'b'
assert User.objects.count() == 1
78 changes: 78 additions & 0 deletions core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import hashlib
import hmac
from datetime import datetime

import requests
from django.conf import settings
from django.contrib.auth import get_user_model
from django.http import HttpRequest
from django.shortcuts import render
from django.utils.safestring import mark_safe
from markdown import markdown

from core.bot import bot

User = get_user_model()


def render_index(request, **extra_context):
with open('README.md', 'r') as f:
readme = markdown(f.read(), extensions=['fenced_code'])
readme = mark_safe(readme)
return render(
request,
'index.html',
context={
'readme': readme,
'bot_username': bot.username,
**extra_context,
}
)


def get_message(data: dict):
return '\n'.join(map(
lambda e: f"{e}={data[e]}",
sorted(data),
))


def validate_data(data: dict):
if 'hash' not in data:
return False
valid_hash = data.pop('hash')
string = get_message(data)
secret_key = hashlib.sha256(settings.TELEGRAM_BOT_TOKEN.encode()).digest()
hash = hmac.new(secret_key, string.encode(), digestmod=hashlib.sha256).hexdigest()
return valid_hash == hash


def get_user(data: dict):
# can't user update_or_create because user must be create with specific method
defaults = {
'first_name': data.get('first_name'),
'last_name': data.get('last_name', ''),
'photo_url': data.get('photo_url'),
'is_active': True,
}
if 'auth_date' in data:
defaults['auth_date'] = datetime.fromtimestamp(int(data['auth_date']))
try:
user = User.objects.get(username=data['id'])
for key, value in defaults.items():
setattr(user, key, value)
user.save()
except User.DoesNotExist:
user = User.objects.create_user(username=data['id'], **defaults)
return user


def verify_public_key(request: HttpRequest):
# todo
requests.get('https://api.travis...')
return True


def format_build_report(data: dict):
# todo
return 'report'
71 changes: 21 additions & 50 deletions core/views.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,32 @@
import hashlib
import hmac
from datetime import datetime
import logging

from django.conf import settings
from django.contrib.auth import login, logout, get_user_model
from django.contrib.auth import get_user_model, login, logout
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render, redirect, get_object_or_404
from django.utils.safestring import mark_safe
from markdown import markdown

from django.shortcuts import get_object_or_404, redirect
from telegram import ParseMode

from core.bot import bot
from core.utils import (
render_index,
validate_data,
get_user,
verify_public_key,
format_build_report,
)

logger = logging.getLogger(__name__)
User = get_user_model()


def render_index(request, **extra_context):
with open('README.md', 'r') as f:
readme = markdown(f.read(), extensions=['fenced_code'])
readme = mark_safe(readme)
return render(request, 'index.html', context={'readme': readme, **extra_context})


def index_view(request: HttpRequest):
if request.user.is_authenticated:
return redirect('core:user', user_id=request.user.username)
return render_index(request)


def validate_data(data: dict):
if 'hash' not in data:
return False
valid_hash = data.pop('hash')
string = '\n'.join(map(
lambda e: f"{e}={data[e]}",
sorted(data),
))
secret_key = hashlib.sha256(settings.TELEGRAM_BOT_TOKEN.encode()).digest()
hash = hmac.new(secret_key, string.encode(), digestmod=hashlib.sha256).hexdigest()
return valid_hash == hash


def get_user(data: dict):
user = User.objects.filter(username=data['id']).first()
if not user:
user = User.objects.create_user(
username=data['id'],
first_name=data.get('first_name'),
last_name=data.get('last_name', ''),
photo_url=data.get('photo_url'),
auth_date=datetime.fromtimestamp(int(data['auth_date']))
)
user.is_active = True
user.save()
return user


def login_success_view(request):
data = {key: value for key, value in request.GET.items()}
valid = validate_data(data)
if not valid:
if not validate_data(data):
return HttpResponse("invalid hash", status=400)

user = get_user(data)
Expand All @@ -67,14 +37,15 @@ def login_success_view(request):
def user_hook_view(request: HttpRequest, user_id: str):
if request.method == 'POST':
user = get_object_or_404(User, username=user_id, is_active=True)
print(user)
# todo: verify public key
# send status to user
if not verify_public_key(request):
return HttpResponse('bad signature', status=400)
text = format_build_report({})
bot.send_message(chat_id=user.username, text=text, parse_mode=ParseMode.MARKDOWN)
return HttpResponse('ok')
return render_index(request)


def logout_view(request):
def logout_view(request: HttpRequest):
u = request.user
if u.is_authenticated:
u.is_active = False
Expand Down
3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
DJANGO_SETTINGS_MODULE = travis_tg.settings
python_files = tests.py test_*.py *_tests.py
16 changes: 16 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
asn1crypto==0.24.0
atomicwrites==1.3.0
attrs==19.1.0
certifi==2019.6.16
cffi==1.12.3
chardet==3.0.4
codecov==2.0.15
coverage==4.5.4
cryptography==2.7
dj-database-url==0.5.0
Django==2.2.4
future==0.17.1
gunicorn==19.9.0
idna==2.8
importlib-metadata==0.19
Markdown==3.1.1
more-itertools==7.2.0
packaging==19.1
pluggy==0.12.0
psycopg2-binary==2.8.3
py==1.8.0
pycparser==2.19
pyparsing==2.4.2
pytest==5.0.1
pytest-cov==2.7.1
pytest-django==3.5.1
pytest-mock==1.10.4
python-telegram-bot==12.0.0b1
pytz==2019.2
requests==2.22.0
six==1.12.0
sqlparse==0.3.0
tornado==6.0.3
urllib3==1.25.3
wcwidth==0.1.7
zipp==0.5.2
2 changes: 1 addition & 1 deletion templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
<script
async
src="https://telegram.org/js/telegram-widget.js?7"
data-telegram-login="dummy2105_bot"
data-telegram-login="{{ bot_username }}"
data-size="large"
data-auth-url="/login_success"
data-request-access="write"
Expand Down

0 comments on commit 6aec910

Please sign in to comment.