Added minimal first version (#1)
Reviewed-on: https://git.cuzo.dev/Parra/crypto-reporter/pulls/1
This commit is contained in:
parent
821f55d31a
commit
0224e02f6a
7 changed files with 480 additions and 0 deletions
152
.drone.yml
Normal file
152
.drone.yml
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: arm32
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: arm
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Build arm/v7
|
||||||
|
image: registry.cuzo.dev/plugins/docker
|
||||||
|
privileged: true
|
||||||
|
settings:
|
||||||
|
repo: parrazam/crypto-reporter
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-arm
|
||||||
|
mirror: https://registry.cuzo.dev
|
||||||
|
username:
|
||||||
|
from_secret: DOCKER_REGISTRY_USER
|
||||||
|
password:
|
||||||
|
from_secret: DOCKER_REGISTRY_PASSWORD
|
||||||
|
|
||||||
|
image_pull_secrets:
|
||||||
|
- PRIVATE_DOCKER_REGISTRY
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: amd64
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Build amd64
|
||||||
|
image: registry.cuzo.dev/plugins/docker
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- name: manifest
|
||||||
|
path: docker
|
||||||
|
settings:
|
||||||
|
repo: parrazam/crypto-reporter
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-amd64
|
||||||
|
mirror: https://registry.cuzo.dev
|
||||||
|
username:
|
||||||
|
from_secret: DOCKER_REGISTRY_USER
|
||||||
|
password:
|
||||||
|
from_secret: DOCKER_REGISTRY_PASSWORD
|
||||||
|
|
||||||
|
image_pull_secrets:
|
||||||
|
- PRIVATE_DOCKER_REGISTRY
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: manifest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Upload manifest
|
||||||
|
image: registry.cuzo.dev/plugins/manifest
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- name: manifest
|
||||||
|
path: docker
|
||||||
|
settings:
|
||||||
|
target: parrazam/crypto-reporter
|
||||||
|
template: parrazam/crypto-reporter:OS-ARCH
|
||||||
|
auto_tag: true
|
||||||
|
ignore_missing: true
|
||||||
|
username:
|
||||||
|
from_secret: DOCKER_REGISTRY_USER
|
||||||
|
password:
|
||||||
|
from_secret: DOCKER_REGISTRY_PASSWORD
|
||||||
|
platforms:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- amd64
|
||||||
|
- arm32
|
||||||
|
|
||||||
|
image_pull_secrets:
|
||||||
|
- PRIVATE_DOCKER_REGISTRY
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: release
|
||||||
|
steps:
|
||||||
|
- name: release
|
||||||
|
image: registry.cuzo.dev/plugins/gitea-release
|
||||||
|
settings:
|
||||||
|
api_key:
|
||||||
|
from_secret: DRONE_API_KEY
|
||||||
|
base_url: https://git.cuzo.dev
|
||||||
|
title:
|
||||||
|
from_secret: DRONE_SEMVER
|
||||||
|
when:
|
||||||
|
ref:
|
||||||
|
- refs/tags/*
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- manifest
|
||||||
|
|
||||||
|
image_pull_secrets:
|
||||||
|
- PRIVATE_DOCKER_REGISTRY
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: Notify result
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: send telegram notification
|
||||||
|
image: registry.cuzo.dev/appleboy/drone-telegram
|
||||||
|
when:
|
||||||
|
status: [success, failure]
|
||||||
|
settings:
|
||||||
|
to:
|
||||||
|
from_secret: TG_USER
|
||||||
|
token:
|
||||||
|
from_secret: TG_TOKEN
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- manifest
|
||||||
|
- release
|
||||||
|
|
||||||
|
image_pull_secrets:
|
||||||
|
- PRIVATE_DOCKER_REGISTRY
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
162
.gitignore
vendored
Normal file
162
.gitignore
vendored
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
# ---> Python
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
.idea/
|
||||||
|
|
9
Dockerfile
Normal file
9
Dockerfile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
FROM python:3-alpine
|
||||||
|
|
||||||
|
# Install dependencies:
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Run the application:
|
||||||
|
COPY main.py .
|
||||||
|
CMD ["python", "main.py"]
|
44
README.md
44
README.md
|
@ -1 +1,45 @@
|
||||||
# crypto-reporter
|
# crypto-reporter
|
||||||
|
|
||||||
|
[![Build Status](https://drone.cuzo.dev/api/badges/Parra/crypto-reporter/status.svg)](https://drone.cuzo.dev/Parra/crypto-reporter)
|
||||||
|
|
||||||
|
Obtiene y publica precios de criptomonedas.
|
||||||
|
|
||||||
|
## Instalación y ejecución desde CLI
|
||||||
|
|
||||||
|
Descargar el repositorio en una nueva carpeta:
|
||||||
|
|
||||||
|
````shell
|
||||||
|
git clone https://git.cuzo.dev/Parra/crypto-reporter.git
|
||||||
|
````
|
||||||
|
|
||||||
|
Copiar el fichero `env_file` a `.env` y abrirlo con un editor de textos y editarlo
|
||||||
|
con la información del servidor MQTT, así como editar los tópicos que se quieran usar
|
||||||
|
y el tiempo de refresco.
|
||||||
|
|
||||||
|
Dar permisos de ejecución al fichero principal con `chmod +x main.py` y ejecutar con `./main.py`.
|
||||||
|
|
||||||
|
## Instalación y ejecución desde Docker
|
||||||
|
|
||||||
|
Crear una carpeta (por ejemplo `crypto-reporter`) y acceder a ella.
|
||||||
|
|
||||||
|
Crear un fichero `.env` con el contenido del fichero `env_file` de este repositorio y
|
||||||
|
editarlo con la información del servidor MQTT.
|
||||||
|
|
||||||
|
Crear un fichero `docker-compose.yml` como el siguiente:
|
||||||
|
````yml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
crypto-reporter:
|
||||||
|
image: parrazam/crypto-reporter
|
||||||
|
container_name: crypto-reporter
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./.env:/.env:ro
|
||||||
|
````
|
||||||
|
|
||||||
|
Ejecutar con el comando `docker compose up -d`.
|
||||||
|
|
||||||
|
## Menciones
|
||||||
|
|
||||||
|
La API se extrae de Coingecko y se puede consultar aquí: https://www.coingecko.com/en/api/documentation
|
21
env_file
Normal file
21
env_file
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Log configuration
|
||||||
|
LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
# Crypto configuration
|
||||||
|
CRYPTOS_TO_PUBLISH=bitcoin,ethereum
|
||||||
|
CURRENCIES=eur,usd
|
||||||
|
|
||||||
|
# Broker configuration
|
||||||
|
MQTT_SERVER=
|
||||||
|
MQTT_PORT=
|
||||||
|
MQTT_USER=
|
||||||
|
MQTT_PASSWORD=
|
||||||
|
MQTT_CLIENT_ID=
|
||||||
|
|
||||||
|
# Topic configuration
|
||||||
|
MQTT_TOPIC_PREFIX=
|
||||||
|
|
||||||
|
MQTT_TOPIC_STATE=${MQTT_TOPIC_PREFIX}/state
|
||||||
|
MQTT_TOPIC_DATA=${MQTT_TOPIC_PREFIX}/data
|
||||||
|
|
||||||
|
MQTT_PUBLISH_DELAY=60
|
89
main.py
Normal file
89
main.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
# !/usr/bin/env python3
|
||||||
|
|
||||||
|
"""Crypto reporter"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import time
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
|
|
||||||
|
load_dotenv() # Cargamos las variables de entorno necesarias
|
||||||
|
|
||||||
|
LOG_LEVEL = os.getenv('LOG_LEVEL', logging.INFO)
|
||||||
|
|
||||||
|
logging.basicConfig(level=LOG_LEVEL, format='[%(levelname)s] %(asctime)s - %(message)s')
|
||||||
|
|
||||||
|
BASE_API = 'https://api.coingecko.com/api/v3'
|
||||||
|
|
||||||
|
MQTT_TOPIC_DATA = os.getenv('MQTT_TOPIC_DATA', 'crypto-reporter/data')
|
||||||
|
MQTT_TOPIC_STATE = os.getenv('MQTT_TOPIC_STATE', 'crypto-reporter/state')
|
||||||
|
|
||||||
|
MQTT_PUBLISH_DELAY = int(os.getenv('MQTT_PUBLISH_DELAY', 60))
|
||||||
|
MQTT_CLIENT_ID = os.getenv('MQTT_CLIENT_ID', 'crypto-reporter')
|
||||||
|
|
||||||
|
MQTT_SERVER = os.getenv('MQTT_SERVER', 'localhost')
|
||||||
|
MQTT_PORT = int(os.getenv('MQTT_PORT', '1883'))
|
||||||
|
MQTT_USER = os.getenv('MQTT_USER', '')
|
||||||
|
MQTT_PASSWORD = os.getenv('MQTT_PASSWORD', '')
|
||||||
|
|
||||||
|
CRYPTOS_TO_PUBLISH = os.getenv('CRYPTOS_TO_PUBLISH', 'bitcoin')
|
||||||
|
CURRENCIES = os.getenv('CURRENCIES', 'eur')
|
||||||
|
|
||||||
|
|
||||||
|
def on_connect(client, userdata, flags, rc):
|
||||||
|
logging.info("Connected to MQTT server.")
|
||||||
|
client.publish(MQTT_TOPIC_STATE, 'connected', 1, True)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
logging.info(f'Connecting to MQTT host {MQTT_SERVER}:{MQTT_PORT}...')
|
||||||
|
mqttc = mqtt.Client(MQTT_CLIENT_ID)
|
||||||
|
mqttc.username_pw_set(MQTT_USER, MQTT_PASSWORD)
|
||||||
|
mqttc.will_set(MQTT_TOPIC_STATE, 'disconnected', 1, True)
|
||||||
|
mqttc.on_connect = on_connect
|
||||||
|
|
||||||
|
mqttc.connect(MQTT_SERVER, MQTT_PORT, 60)
|
||||||
|
mqttc.loop_start()
|
||||||
|
last_msg_time = time.time()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
x = requests.get(BASE_API + '/simple/price',
|
||||||
|
params={'ids': CRYPTOS_TO_PUBLISH,
|
||||||
|
'vs_currencies': CURRENCIES})
|
||||||
|
logging.debug(f'HTTP response: {x.status_code}')
|
||||||
|
publish_data(mqttc, x.text)
|
||||||
|
|
||||||
|
delay_gap = time.time() - last_msg_time
|
||||||
|
if delay_gap < MQTT_PUBLISH_DELAY:
|
||||||
|
time.sleep(MQTT_PUBLISH_DELAY - delay_gap)
|
||||||
|
last_msg_time = time.time()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logging.info("exiting...")
|
||||||
|
exit(0)
|
||||||
|
except Exception:
|
||||||
|
logging.exception("something went wrong.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def publish_data(mqttc, raw):
|
||||||
|
try:
|
||||||
|
logging.debug(f'RAW message to publish: {raw}')
|
||||||
|
data = json.loads(raw)
|
||||||
|
for crypto in data:
|
||||||
|
currencies = data[crypto]
|
||||||
|
for currency in currencies:
|
||||||
|
mqttc.publish(MQTT_TOPIC_DATA + f'/{crypto}/{currency}', data[crypto][currency], 1, True)
|
||||||
|
except AttributeError:
|
||||||
|
logging.exception("Error decoding JSON")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
logging.info('Starting crypto broker')
|
||||||
|
main()
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
paho-mqtt==1.6.1
|
||||||
|
python-dotenv==0.21.0
|
||||||
|
requests==2.28.1
|
Loading…
Reference in a new issue