mirror of
https://github.com/nikdoof/smsbot.git
synced 2025-12-13 10:02:15 +00:00
Compare commits
56 Commits
smsbot-hel
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9d1f9cc27 | ||
|
|
ecf4483937 | ||
|
|
d9e52d13a5 | ||
|
|
3f3e232fdb | ||
|
|
771bbfec91 | ||
|
|
afc5194cfb | ||
|
|
6696976012 | ||
|
|
716d4355fc | ||
|
|
ff2424de8c | ||
|
|
e8dc6b6bbc | ||
|
|
ec77ebbdb5 | ||
|
e02a3169a4
|
|||
| ade5e32387 | |||
|
|
3f0753b1b7 | ||
| eb8bce763d | |||
|
fc2aa86b89
|
|||
|
51eff4a4fe
|
|||
|
f49037ab09
|
|||
|
5984bdd76f
|
|||
|
414e50e559
|
|||
|
6d4d86447c
|
|||
|
5cacd8e23a
|
|||
|
|
81232914cd | ||
| 5980bc67dd | |||
| bf0c3e51a7 | |||
| 5dae3d007d | |||
|
|
efe96bf152 | ||
|
|
d575bfe8d2 | ||
|
4a2dd76401
|
|||
|
899b88e907
|
|||
|
000f0932d8
|
|||
|
3d28716817
|
|||
|
74dac7603e
|
|||
|
076e2464a0
|
|||
|
85c72462c4
|
|||
|
|
6651827018 | ||
| 41971aabdf | |||
| befa3df48d | |||
| d504dfb95d | |||
| e51541c79a | |||
| cd3699f185 | |||
| 7d7aa97c24 | |||
|
|
d51c6fbe73 | ||
| 21276e1c24 | |||
|
|
4e15d4a8ed | ||
| b1bdcb456a | |||
| f7a151235a | |||
| 1d8b177cb4 | |||
|
|
3389144484 | ||
|
|
aa3ca58b58 | ||
|
|
2854626c27 | ||
|
|
dc0745ce76 | ||
|
|
19f8183ddc | ||
|
|
f51928cb4b | ||
|
|
80be469a90 | ||
|
|
076216e089 |
3
.github/renovate.json
vendored
3
.github/renovate.json
vendored
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": [
|
||||||
"config:base"
|
"config:base",
|
||||||
|
"github>nikdoof/renovate-config:python"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
8
.github/workflows/build-container.yaml
vendored
8
.github/workflows/build-container.yaml
vendored
@@ -12,11 +12,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
id: docker_build
|
id: docker_build
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
|
|||||||
31
.github/workflows/lint.yaml
vendored
31
.github/workflows/lint.yaml
vendored
@@ -1,6 +1,6 @@
|
|||||||
name: Lint
|
name: Lint
|
||||||
|
|
||||||
'on':
|
"on":
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
@@ -9,13 +9,26 @@ name: Lint
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-linters:
|
lint:
|
||||||
name: Run linters
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.9"]
|
||||||
steps:
|
steps:
|
||||||
- name: Check out Git repository
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v3
|
- name: Install Task
|
||||||
|
uses: arduino/setup-task@v2
|
||||||
- name: wemake-python-styleguide
|
with:
|
||||||
uses: wemake-services/wemake-python-styleguide@0.17.0
|
version: 3.x
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install Poetry
|
||||||
|
uses: snok/install-poetry@v1
|
||||||
|
with:
|
||||||
|
virtualenvs-create: true
|
||||||
|
virtualenvs-in-project: true
|
||||||
|
- name: Lint with ruff
|
||||||
|
run: |
|
||||||
|
task python:lint
|
||||||
|
|||||||
6
.github/workflows/release-chart.yaml
vendored
6
.github/workflows/release-chart.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@@ -21,12 +21,12 @@ jobs:
|
|||||||
git config user.name "$GITHUB_ACTOR"
|
git config user.name "$GITHUB_ACTOR"
|
||||||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||||
- name: Install Helm
|
- name: Install Helm
|
||||||
uses: azure/setup-helm@v3
|
uses: azure/setup-helm@v4
|
||||||
with:
|
with:
|
||||||
version: v3.6.3
|
version: v3.6.3
|
||||||
|
|
||||||
- name: Run chart-releaser
|
- name: Run chart-releaser
|
||||||
uses: helm/chart-releaser-action@v1.5.0
|
uses: helm/chart-releaser-action@v1.7.0
|
||||||
env:
|
env:
|
||||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
CR_RELEASE_NAME_TEMPLATE: "smsbot-helm-chart-{{ .Version }}"
|
CR_RELEASE_NAME_TEMPLATE: "smsbot-helm-chart-{{ .Version }}"
|
||||||
|
|||||||
24
.github/workflows/release.yaml
vendored
24
.github/workflows/release.yaml
vendored
@@ -1,4 +1,3 @@
|
|||||||
---
|
|
||||||
name: Release
|
name: Release
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -10,20 +9,25 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: "3.9"
|
||||||
- run: pip install -r requirements-dev.txt
|
|
||||||
|
|
||||||
- name: Build Assets
|
- name: Install Poetry
|
||||||
run: python setup.py sdist bdist_wheel
|
uses: snok/install-poetry@v1
|
||||||
|
with:
|
||||||
|
virtualenvs-create: true
|
||||||
|
virtualenvs-in-project: true
|
||||||
|
|
||||||
|
- name: Build Release
|
||||||
|
run: poetry build
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
name: "Version ${{ github.ref_name }}"
|
name: "Version ${{ github.ref_name }}"
|
||||||
generate_release_notes: true
|
|
||||||
files: |
|
files: |
|
||||||
dist/*
|
dist/*
|
||||||
|
|||||||
26
Dockerfile
26
Dockerfile
@@ -1,5 +1,21 @@
|
|||||||
FROM python:3.11-alpine
|
FROM python:3.9-alpine AS base
|
||||||
WORKDIR /app
|
|
||||||
COPY . /app
|
# Builder
|
||||||
RUN pip install .
|
FROM base AS builder
|
||||||
ENTRYPOINT smsbot
|
|
||||||
|
ENV POETRY_VERSION=2.1.1
|
||||||
|
|
||||||
|
RUN apk add build-base unzip wget python3-dev libffi-dev rust cargo openssl-dev && pip install "poetry==$POETRY_VERSION" && poetry self add poetry-plugin-bundle
|
||||||
|
WORKDIR /src
|
||||||
|
COPY poetry.lock pyproject.toml README.md /src/
|
||||||
|
COPY smsbot /src/smsbot
|
||||||
|
RUN poetry bundle venv /runtime
|
||||||
|
|
||||||
|
|
||||||
|
# Final container
|
||||||
|
FROM base AS runtime
|
||||||
|
|
||||||
|
COPY --from=builder /runtime /runtime
|
||||||
|
ENV PATH=/runtime/bin:$PATH
|
||||||
|
EXPOSE 80/tcp
|
||||||
|
CMD ["smsbot"]
|
||||||
17
Taskfile.yaml
Normal file
17
Taskfile.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
version: 3
|
||||||
|
tasks:
|
||||||
|
python:env:
|
||||||
|
cmds:
|
||||||
|
- poetry install --with=dev,github
|
||||||
|
python:lint:
|
||||||
|
desc: Lint Python files
|
||||||
|
deps:
|
||||||
|
- python:env
|
||||||
|
cmds:
|
||||||
|
- poetry run ruff check --output-format=github --select=E9,F63,F7,F82 --target-version=py39 .
|
||||||
|
- poetry run ruff check --output-format=github --target-version=py39 .
|
||||||
|
|
||||||
|
docker:build:
|
||||||
|
desc: Build the container using Docker
|
||||||
|
cmds:
|
||||||
|
- docker build . -t smsbot:latest
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
appVersion: 0.0.4
|
appVersion: 0.0.5
|
||||||
description: A simple Telegram bot to receive SMS messages.
|
description: A simple Telegram bot to receive SMS messages.
|
||||||
name: smsbot
|
name: smsbot
|
||||||
version: 0.0.5
|
version: 0.0.6
|
||||||
kubeVersion: ">=1.19.0-0"
|
kubeVersion: ">=1.19.0-0"
|
||||||
keywords:
|
keywords:
|
||||||
- smsbot
|
- smsbot
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ image:
|
|||||||
# -- image pull policy
|
# -- image pull policy
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
# -- image tag
|
# -- image tag
|
||||||
tag: 0.0.4
|
tag: 0.0.5
|
||||||
|
|
||||||
# -- environment variables.
|
# -- environment variables.
|
||||||
# @default -- See below
|
# @default -- See below
|
||||||
|
|||||||
1544
poetry.lock
generated
Normal file
1544
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
pyproject.toml
Normal file
38
pyproject.toml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
[project]
|
||||||
|
name = "smsbot"
|
||||||
|
version = "0.0.6"
|
||||||
|
description = "A simple Telegram bot to receive SMS messages."
|
||||||
|
authors = [{ name = "Andrew Williams", email = "andy@tensixtyone.com" }]
|
||||||
|
license = { text = "MIT" }
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.9,<3.10"
|
||||||
|
dependencies = [
|
||||||
|
"flask (>=3.1.0,<4.0.0)",
|
||||||
|
"waitress (>=3.0.2,<4.0.0)",
|
||||||
|
"twilio (>=9.4.6,<10.0.0)",
|
||||||
|
"python-telegram-bot (<20)",
|
||||||
|
"prometheus-client (>=0.21.1,<0.22.0)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.poetry]
|
||||||
|
packages = [{ include = "smsbot" }]
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
smsbot = "smsbot.cli:main"
|
||||||
|
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
pytest = "^8.0.0"
|
||||||
|
ruff = "^0.12.0"
|
||||||
|
pytest-mock = "^3.12.0"
|
||||||
|
pytest-flask = "^1.3.0"
|
||||||
|
|
||||||
|
[tool.poetry.group.github]
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[tool.poetry.group.github.dependencies]
|
||||||
|
pytest-github-actions-annotate-failures = "^0.3.0"
|
||||||
@@ -1 +0,0 @@
|
|||||||
wheel
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
flask
|
|
||||||
waitress
|
|
||||||
twilio
|
|
||||||
python-telegram-bot
|
|
||||||
prometheus_client
|
|
||||||
36
setup.cfg
36
setup.cfg
@@ -1,36 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = smsbot
|
|
||||||
version = 0.0.4
|
|
||||||
description = A simple Telegram bot to receive SMS messages.
|
|
||||||
long_description = file: README.md, LICENSE
|
|
||||||
license = MIT
|
|
||||||
license_file = LICENSE
|
|
||||||
classifiers =
|
|
||||||
Framework :: Flask
|
|
||||||
License :: OSI Approved :: MIT License
|
|
||||||
Programming Language :: Python :: 3
|
|
||||||
Programming Language :: Python :: 3.11
|
|
||||||
|
|
||||||
[options]
|
|
||||||
zip_safe = False
|
|
||||||
include_package_data = True
|
|
||||||
packages = smsbot
|
|
||||||
install_requires =
|
|
||||||
flask
|
|
||||||
waitress
|
|
||||||
twilio
|
|
||||||
python-telegram-bot<=14
|
|
||||||
prometheus_client
|
|
||||||
|
|
||||||
[options.entry_points]
|
|
||||||
console_scripts =
|
|
||||||
smsbot = smsbot.cli:main
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
format = wemake
|
|
||||||
ignore = D,E501,WPS432,WPS323,WPS2,S104,WPS412,WPS100
|
|
||||||
max-line-length = 120
|
|
||||||
exclude = setup.py,env,build
|
|
||||||
|
|
||||||
[darglint]
|
|
||||||
docstring_style=sphinx
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from smsbot.cli import main
|
from smsbot.cli import main
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -2,27 +2,33 @@ import argparse
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import pkg_resources
|
|
||||||
|
|
||||||
from smsbot.telegram import TelegramSmsBot
|
from smsbot.telegram import TelegramSmsBot
|
||||||
|
from smsbot.utils import get_smsbot_version
|
||||||
from smsbot.webhook_handler import TwilioWebhookHandler
|
from smsbot.webhook_handler import TwilioWebhookHandler
|
||||||
|
|
||||||
pkg_version = pkg_resources.require('smsbot')[0].version
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser('smsbot')
|
parser = argparse.ArgumentParser("smsbot")
|
||||||
parser.add_argument('--listen-host', default=os.environ.get('SMSBOT_LISTEN_HOST') or '0.0.0.0')
|
parser.add_argument(
|
||||||
parser.add_argument('--listen-port', default=os.environ.get('SMSBOT_LISTEN_PORT') or '80')
|
"--listen-host", default=os.environ.get("SMSBOT_LISTEN_HOST") or "0.0.0.0"
|
||||||
parser.add_argument('--telegram-bot-token', default=os.environ.get('SMSBOT_TELEGRAM_BOT_TOKEN'))
|
)
|
||||||
parser.add_argument('--owner-id', default=os.environ.get('SMSBOT_OWNER_ID'))
|
parser.add_argument(
|
||||||
parser.add_argument('--default-subscribers', default=os.environ.get('SMSBOT_DEFAULT_SUBSCRIBERS'))
|
"--listen-port", default=os.environ.get("SMSBOT_LISTEN_PORT") or "80"
|
||||||
parser.add_argument('--log-level', default='INFO')
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--telegram-bot-token", default=os.environ.get("SMSBOT_TELEGRAM_BOT_TOKEN")
|
||||||
|
)
|
||||||
|
parser.add_argument("--owner-id", default=os.environ.get("SMSBOT_OWNER_ID"))
|
||||||
|
parser.add_argument(
|
||||||
|
"--default-subscribers", default=os.environ.get("SMSBOT_DEFAULT_SUBSCRIBERS")
|
||||||
|
)
|
||||||
|
parser.add_argument("--log-level", default="INFO")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# TODO: Replace for Py >=3.11
|
||||||
logging.basicConfig(level=logging.getLevelName(args.log_level))
|
logging.basicConfig(level=logging.getLevelName(args.log_level))
|
||||||
logging.info('smsbot v%s', pkg_version)
|
logging.info("smsbot v%s", get_smsbot_version())
|
||||||
logging.debug('Arguments: %s', args)
|
logging.debug("Arguments: %s", args)
|
||||||
|
|
||||||
# Start bot
|
# Start bot
|
||||||
telegram_bot = TelegramSmsBot(args.telegram_bot_token)
|
telegram_bot = TelegramSmsBot(args.telegram_bot_token)
|
||||||
@@ -31,11 +37,11 @@ def main():
|
|||||||
if args.owner_id:
|
if args.owner_id:
|
||||||
telegram_bot.set_owner(args.owner_id)
|
telegram_bot.set_owner(args.owner_id)
|
||||||
else:
|
else:
|
||||||
logging.warning('No Owner ID is set, which is not a good idea...')
|
logging.warning("No Owner ID is set, which is not a good idea...")
|
||||||
|
|
||||||
# Add default subscribers
|
# Add default subscribers
|
||||||
if args.default_subscribers:
|
if args.default_subscribers:
|
||||||
for chat_id in args.default_subscribers.split(','):
|
for chat_id in args.default_subscribers.split(","):
|
||||||
telegram_bot.add_subscriber(chat_id)
|
telegram_bot.add_subscriber(chat_id)
|
||||||
|
|
||||||
telegram_bot.start()
|
telegram_bot.start()
|
||||||
|
|||||||
@@ -5,33 +5,40 @@ from telegram.ext import CommandHandler, Updater
|
|||||||
|
|
||||||
from smsbot.utils import get_smsbot_version
|
from smsbot.utils import get_smsbot_version
|
||||||
|
|
||||||
REQUEST_TIME = Summary('telegram_request_processing_seconds', 'Time spent processing request')
|
REQUEST_TIME = Summary(
|
||||||
COMMAND_COUNT = Counter('telegram_command_count', 'Total number of commands processed')
|
"telegram_request_processing_seconds", "Time spent processing request"
|
||||||
|
)
|
||||||
|
COMMAND_COUNT = Counter("telegram_command_count", "Total number of commands processed")
|
||||||
|
|
||||||
|
|
||||||
class TelegramSmsBot(object):
|
class TelegramSmsBot(object):
|
||||||
|
def __init__(
|
||||||
def __init__(self, telegram_token, allow_subscribing=False, owner=None, subscribers=None):
|
self, telegram_token, allow_subscribing=False, owner=None, subscribers=None
|
||||||
|
):
|
||||||
self.logger = logging.getLogger(self.__class__.__name__)
|
self.logger = logging.getLogger(self.__class__.__name__)
|
||||||
self.bot_token = telegram_token
|
self.bot_token = telegram_token
|
||||||
self.subscriber_ids = subscribers or []
|
self.subscriber_ids = subscribers or []
|
||||||
self.set_owner(owner)
|
self.set_owner(owner)
|
||||||
|
|
||||||
self.updater = Updater(self.bot_token, use_context=True)
|
self.updater = Updater(self.bot_token, use_context=True)
|
||||||
self.updater.dispatcher.add_handler(CommandHandler('help', self.help_handler))
|
self.updater.dispatcher.add_handler(CommandHandler("help", self.help_handler))
|
||||||
self.updater.dispatcher.add_handler(CommandHandler('start', self.help_handler))
|
self.updater.dispatcher.add_handler(CommandHandler("start", self.help_handler))
|
||||||
|
|
||||||
if allow_subscribing:
|
if allow_subscribing:
|
||||||
self.updater.dispatcher.add_handler(CommandHandler('subscribe', self.subscribe_handler))
|
self.updater.dispatcher.add_handler(
|
||||||
self.updater.dispatcher.add_handler(CommandHandler('unsubscribe', self.unsubscribe_handler))
|
CommandHandler("subscribe", self.subscribe_handler)
|
||||||
|
)
|
||||||
|
self.updater.dispatcher.add_handler(
|
||||||
|
CommandHandler("unsubscribe", self.unsubscribe_handler)
|
||||||
|
)
|
||||||
|
|
||||||
self.updater.dispatcher.add_error_handler(self.error_handler)
|
self.updater.dispatcher.add_error_handler(self.error_handler)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.logger.info('Starting bot...')
|
self.logger.info("Starting bot...")
|
||||||
self.updater.start_polling()
|
self.updater.start_polling()
|
||||||
self.bot = self.updater.bot
|
self.bot = self.updater.bot
|
||||||
self.logger.info('Bot Ready')
|
self.logger.info("Bot Ready")
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.updater.stop()
|
self.updater.stop()
|
||||||
@@ -39,43 +46,57 @@ class TelegramSmsBot(object):
|
|||||||
@REQUEST_TIME.time()
|
@REQUEST_TIME.time()
|
||||||
def help_handler(self, update, context):
|
def help_handler(self, update, context):
|
||||||
"""Send a message when the command /help is issued."""
|
"""Send a message when the command /help is issued."""
|
||||||
self.logger.info('/help command received in chat: %s', update.message.chat)
|
self.logger.info("/help command received in chat: %s", update.message.chat)
|
||||||
|
|
||||||
commands = []
|
commands = []
|
||||||
for command in self.updater.dispatcher.handlers[0]:
|
for command in self.updater.dispatcher.handlers[0]:
|
||||||
commands.extend(['/{0}'.format(cmd) for cmd in command.command])
|
commands.extend(["/{0}".format(cmd) for cmd in command.command])
|
||||||
|
|
||||||
update.message.reply_markdown('Smsbot v{0}\n\n{1}'.format(get_smsbot_version(), '\n'.join(commands)))
|
update.message.reply_markdown(
|
||||||
|
"Smsbot v{0}\n\n{1}".format(get_smsbot_version(), "\n".join(commands))
|
||||||
|
)
|
||||||
COMMAND_COUNT.inc()
|
COMMAND_COUNT.inc()
|
||||||
|
|
||||||
@REQUEST_TIME.time()
|
@REQUEST_TIME.time()
|
||||||
def subscribe_handler(self, update, context):
|
def subscribe_handler(self, update, context):
|
||||||
self.logger.info('/subscribe command received')
|
self.logger.info("/subscribe command received")
|
||||||
if update.message.chat['id'] not in self.subscriber_ids:
|
if update.message.chat["id"] not in self.subscriber_ids:
|
||||||
self.logger.info('{0} subscribed'.format(update.message.chat['username']))
|
self.logger.info("{0} subscribed".format(update.message.chat["username"]))
|
||||||
self.subscriber_ids.append(update.message.chat['id'])
|
self.subscriber_ids.append(update.message.chat["id"])
|
||||||
self.send_owner('{0} has subscribed'.format(update.message.chat['username']))
|
self.send_owner(
|
||||||
update.message.reply_markdown('You have been subscribed to SMS notifications')
|
"{0} has subscribed".format(update.message.chat["username"])
|
||||||
|
)
|
||||||
|
update.message.reply_markdown(
|
||||||
|
"You have been subscribed to SMS notifications"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
update.message.reply_markdown('You are already subscribed to SMS notifications')
|
update.message.reply_markdown(
|
||||||
|
"You are already subscribed to SMS notifications"
|
||||||
|
)
|
||||||
COMMAND_COUNT.inc()
|
COMMAND_COUNT.inc()
|
||||||
|
|
||||||
@REQUEST_TIME.time()
|
@REQUEST_TIME.time()
|
||||||
def unsubscribe_handler(self, update, context):
|
def unsubscribe_handler(self, update, context):
|
||||||
self.logger.info('/unsubscribe command received')
|
self.logger.info("/unsubscribe command received")
|
||||||
if update.message.chat['id'] in self.subscriber_ids:
|
if update.message.chat["id"] in self.subscriber_ids:
|
||||||
self.logger.info('{0} unsubscribed'.format(update.message.chat['username']))
|
self.logger.info("{0} unsubscribed".format(update.message.chat["username"]))
|
||||||
self.subscriber_ids.remove(update.message.chat['id'])
|
self.subscriber_ids.remove(update.message.chat["id"])
|
||||||
self.send_owner('{0} has unsubscribed'.format(update.message.chat['username']))
|
self.send_owner(
|
||||||
update.message.reply_markdown('You have been unsubscribed to SMS notifications')
|
"{0} has unsubscribed".format(update.message.chat["username"])
|
||||||
|
)
|
||||||
|
update.message.reply_markdown(
|
||||||
|
"You have been unsubscribed to SMS notifications"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
update.message.reply_markdown('You are not subscribed to SMS notifications')
|
update.message.reply_markdown("You are not subscribed to SMS notifications")
|
||||||
COMMAND_COUNT.inc()
|
COMMAND_COUNT.inc()
|
||||||
|
|
||||||
def error_handler(self, update, context):
|
def error_handler(self, update, context):
|
||||||
"""Log Errors caused by Updates."""
|
"""Log Errors caused by Updates."""
|
||||||
self.logger.warning('Update "%s" caused error "%s"', update, context.error)
|
self.logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||||
self.send_owner('Update "%{0}" caused error "{1}"'.format(update, context.error))
|
self.send_owner(
|
||||||
|
'Update "%{0}" caused error "{1}"'.format(update, context.error)
|
||||||
|
)
|
||||||
|
|
||||||
def send_message(self, message, chat_id):
|
def send_message(self, message, chat_id):
|
||||||
self.bot.sendMessage(text=message, chat_id=chat_id)
|
self.bot.sendMessage(text=message, chat_id=chat_id)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import pkg_resources
|
from importlib.metadata import version
|
||||||
|
|
||||||
|
|
||||||
def get_smsbot_version():
|
def get_smsbot_version():
|
||||||
return pkg_resources.require('smsbot')[0].version
|
return version("smsbot")
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
@@ -10,20 +9,23 @@ from werkzeug.middleware.dispatcher import DispatcherMiddleware
|
|||||||
|
|
||||||
from smsbot.utils import get_smsbot_version
|
from smsbot.utils import get_smsbot_version
|
||||||
|
|
||||||
REQUEST_TIME = Summary('webhook_request_processing_seconds', 'Time spent processing request')
|
REQUEST_TIME = Summary("webhook_request_processing_seconds", "Time spent processing request")
|
||||||
MESSAGE_COUNT = Counter('webhook_message_count', 'Total number of messages processed')
|
MESSAGE_COUNT = Counter("webhook_message_count", "Total number of messages processed")
|
||||||
CALL_COUNT = Counter('webhook_call_count', 'Total number of calls processed')
|
CALL_COUNT = Counter("webhook_call_count", "Total number of calls processed")
|
||||||
|
|
||||||
|
|
||||||
def validate_twilio_request(func):
|
def validate_twilio_request(func):
|
||||||
"""Validates that incoming requests genuinely originated from Twilio"""
|
"""Validates that incoming requests genuinely originated from Twilio"""
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def decorated_function(*args, **kwargs): # noqa: WPS430
|
def decorated_function(*args, **kwargs): # noqa: WPS430
|
||||||
# Create an instance of the RequestValidator class
|
# Create an instance of the RequestValidator class
|
||||||
twilio_token = os.environ.get('SMSBOT_TWILIO_AUTH_TOKEN')
|
twilio_token = os.environ.get("SMSBOT_TWILIO_AUTH_TOKEN")
|
||||||
|
|
||||||
if not twilio_token:
|
if not twilio_token:
|
||||||
current_app.logger.warning('Twilio request validation skipped due to SMSBOT_TWILIO_AUTH_TOKEN missing')
|
current_app.logger.warning(
|
||||||
|
"Twilio request validation skipped due to SMSBOT_TWILIO_AUTH_TOKEN missing"
|
||||||
|
)
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
validator = RequestValidator(twilio_token)
|
validator = RequestValidator(twilio_token)
|
||||||
@@ -33,7 +35,7 @@ def validate_twilio_request(func):
|
|||||||
request_valid = validator.validate(
|
request_valid = validator.validate(
|
||||||
request.url,
|
request.url,
|
||||||
request.form,
|
request.form,
|
||||||
request.headers.get('X-TWILIO-SIGNATURE', ''),
|
request.headers.get("X-TWILIO-SIGNATURE", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Continue processing the request if it's valid, return a 403 error if
|
# Continue processing the request if it's valid, return a 403 error if
|
||||||
@@ -41,58 +43,67 @@ def validate_twilio_request(func):
|
|||||||
if request_valid or current_app.debug:
|
if request_valid or current_app.debug:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
return abort(403)
|
return abort(403)
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
class TwilioWebhookHandler(object):
|
class TwilioWebhookHandler(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = Flask(self.__class__.__name__)
|
self.app = Flask(self.__class__.__name__)
|
||||||
self.app.add_url_rule('/', 'index', self.index, methods=['GET'])
|
self.app.add_url_rule("/", "index", self.index, methods=["GET"])
|
||||||
self.app.add_url_rule('/health', 'health', self.health, methods=['GET'])
|
self.app.add_url_rule("/health", "health", self.health, methods=["GET"])
|
||||||
self.app.add_url_rule('/message', 'message', self.message, methods=['POST'])
|
self.app.add_url_rule("/message", "message", self.message, methods=["POST"])
|
||||||
self.app.add_url_rule('/call', 'call', self.call, methods=['POST'])
|
self.app.add_url_rule("/call", "call", self.call, methods=["POST"])
|
||||||
|
|
||||||
# Add prometheus wsgi middleware to route /metrics requests
|
# Add prometheus wsgi middleware to route /metrics requests
|
||||||
self.app.wsgi_app = DispatcherMiddleware(self.app.wsgi_app, {
|
self.app.wsgi_app = DispatcherMiddleware(
|
||||||
'/metrics': make_wsgi_app(),
|
self.app.wsgi_app,
|
||||||
})
|
{
|
||||||
|
"/metrics": make_wsgi_app(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def set_bot(self, bot): # noqa: WPS615
|
def set_bot(self, bot): # noqa: WPS615
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
def index(self):
|
def index(self):
|
||||||
return ''
|
return ""
|
||||||
|
|
||||||
@REQUEST_TIME.time()
|
@REQUEST_TIME.time()
|
||||||
def health(self):
|
def health(self):
|
||||||
return {
|
return {
|
||||||
'version': get_smsbot_version(),
|
"version": get_smsbot_version(),
|
||||||
'owner': self.bot.owner_id,
|
"owner": self.bot.owner_id,
|
||||||
'subscribers': self.bot.subscriber_ids,
|
"subscribers": self.bot.subscriber_ids,
|
||||||
}
|
}
|
||||||
|
|
||||||
@REQUEST_TIME.time()
|
@REQUEST_TIME.time()
|
||||||
@validate_twilio_request
|
@validate_twilio_request
|
||||||
def message(self):
|
def message(self):
|
||||||
current_app.logger.info('Received SMS from {From}: {Body}'.format(**request.values.to_dict()))
|
current_app.logger.info(
|
||||||
|
"Received SMS from {From}: {Body}".format(**request.values.to_dict())
|
||||||
|
)
|
||||||
|
|
||||||
message = 'From: {From}\n\n{Body}'.format(**request.values.to_dict())
|
message = "From: {From}\n\n{Body}".format(**request.values.to_dict())
|
||||||
self.bot.send_subscribers(message)
|
self.bot.send_subscribers(message)
|
||||||
|
|
||||||
# Return a blank response
|
# Return a blank response
|
||||||
MESSAGE_COUNT.inc()
|
MESSAGE_COUNT.inc()
|
||||||
return '<response></response>'
|
return "<response></response>"
|
||||||
|
|
||||||
@REQUEST_TIME.time()
|
@REQUEST_TIME.time()
|
||||||
@validate_twilio_request
|
@validate_twilio_request
|
||||||
def call(self):
|
def call(self):
|
||||||
current_app.logger.info('Received Call from {From}'.format(**request.values.to_dict()))
|
current_app.logger.info(
|
||||||
self.bot.send_subscribers('Received Call from {From}, rejecting.'.format(**request.values.to_dict()))
|
"Received Call from {From}".format(**request.values.to_dict())
|
||||||
|
)
|
||||||
|
self.bot.send_subscribers(
|
||||||
|
"Received Call from {From}, rejecting.".format(**request.values.to_dict())
|
||||||
|
)
|
||||||
|
|
||||||
# Always reject calls
|
# Always reject calls
|
||||||
CALL_COUNT.inc()
|
CALL_COUNT.inc()
|
||||||
return '<Response><Reject/></Response>'
|
return "<Response><Reject/></Response>"
|
||||||
|
|
||||||
def serve(self, host='0.0.0.0', port=80, debug=False):
|
def serve(self, host="0.0.0.0", port=80, debug=False):
|
||||||
serve(self.app, host=host, port=port)
|
serve(self.app, host=host, port=port)
|
||||||
|
|||||||
Reference in New Issue
Block a user