mirror of
https://github.com/nikdoof/smsbot.git
synced 2025-12-13 10:02:15 +00:00
Initial import
This commit is contained in:
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Desktop (please complete the following information):**
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Browser [e.g. chrome, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Smartphone (please complete the following information):**
|
||||||
|
- Device: [e.g. iPhone6]
|
||||||
|
- OS: [e.g. iOS8.1]
|
||||||
|
- Browser [e.g. stock browser, safari]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
5
.github/renovate.json
vendored
Normal file
5
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"config:base"
|
||||||
|
]
|
||||||
|
}
|
||||||
32
.github/workflows/build-container.yaml
vendored
Normal file
32
.github/workflows/build-container.yaml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: Build Container
|
||||||
|
|
||||||
|
"on":
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- "[0-9]+.[0-9]+.[0-9]+"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
ghcr.io/${{ github.repository_owner }}/ohayodash:${{ github.ref_name }}
|
||||||
|
ghcr.io/${{ github.repository_owner }}/ohayodash:latest
|
||||||
21
.github/workflows/lint.yaml
vendored
Normal file
21
.github/workflows/lint.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Lint
|
||||||
|
|
||||||
|
'on':
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-linters:
|
||||||
|
name: Run linters
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: wemake-python-styleguide
|
||||||
|
uses: wemake-services/wemake-python-styleguide@0.16.0
|
||||||
27
.github/workflows/release.yaml
vendored
Normal file
27
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
name: Release
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "[0-9]+.[0-9]+.[0-9]+"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
|
- run: pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
- name: Build Assets
|
||||||
|
run: python setup.py sdist bdist_wheel
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
name: "Version ${{ github.ref_name }}"
|
||||||
|
generate_release_notes: true
|
||||||
|
files: |
|
||||||
|
dist/*
|
||||||
104
.gitignore
vendored
Normal file
104
.gitignore
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# 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/
|
||||||
|
*.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/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# 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/
|
||||||
5
Dockerfile
Normal file
5
Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM python:3.8-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . /app
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
ENTRYPOINT python smsbot.py
|
||||||
22
LICENSE
Normal file
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Five B
|
||||||
|
Copyright (c) 2022 Andrew Williams <andy@tensixtyone.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
11
README.md
Normal file
11
README.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# SMSBot
|
||||||
|
|
||||||
|
A simple Telegram bot fo receive SMS messages.
|
||||||
|
|
||||||
|
Forked from [FiveBoroughs/Twilio2Telegram](https://github.com/FiveBoroughs/Twilio2Telegram).
|
||||||
|
|
||||||
|
## Functionality
|
||||||
|
|
||||||
|
This simple tool acts as a webhook receiver for the Twilio API, taking messages sent over and posting them on Telegram to a target chat or channel. This is useful for forwarding on 2FA messages, notification text messages for services that don't support international numbers (**cough** Disney, of all people).
|
||||||
|
|
||||||
|
The bot is designed to run within a Kubernetes environment, but can be operated as a individual container, or as a hand ran service.
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
flask
|
||||||
|
waitress
|
||||||
|
twilio
|
||||||
|
python-telegram-bot
|
||||||
147
smsbot.py
Normal file
147
smsbot.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
try:
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from functools import wraps
|
||||||
|
from flask import Flask, request, abort
|
||||||
|
from waitress import serve
|
||||||
|
from twilio.request_validator import RequestValidator
|
||||||
|
from telegram import ParseMode
|
||||||
|
from telegram.ext import Updater, CommandHandler
|
||||||
|
except ImportError as err:
|
||||||
|
print(f"Failed to import required modules: {err}")
|
||||||
|
|
||||||
|
# FB - Enable logging
|
||||||
|
logging.basicConfig(
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
webhook_listener = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_twilio_request(f):
|
||||||
|
"""Validates that incoming requests genuinely originated from Twilio"""
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
# FB - Create an instance of the RequestValidator class
|
||||||
|
validator = RequestValidator(os.environ.get('TWILIO_AUTH_TOKEN'))
|
||||||
|
|
||||||
|
# FB - Validate the request using its URL, POST data, and X-TWILIO-SIGNATURE header
|
||||||
|
request_valid = validator.validate(
|
||||||
|
request.url,
|
||||||
|
request.form,
|
||||||
|
request.headers.get('X-TWILIO-SIGNATURE', ''))
|
||||||
|
|
||||||
|
# FB - Continue processing the request if it's valid, return a 403 error if it's not
|
||||||
|
if request_valid:
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
logger.error('Invalid twilio request, aborting')
|
||||||
|
return abort(403)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
def tg_help_handler(update, context):
|
||||||
|
"""Send a message when the command /help is issued."""
|
||||||
|
logger.info('/help command received in chat: %s', update.message.chat)
|
||||||
|
update.message.reply_markdown(
|
||||||
|
'Find out more on [Github](https://github.com/FiveBoroughs/Twilio2Telegram)')
|
||||||
|
|
||||||
|
|
||||||
|
def tg_error_handler(update, context):
|
||||||
|
"""Log Errors caused by Updates."""
|
||||||
|
logger.warning('Update "%s" caused error "%s"', update, context.error)
|
||||||
|
|
||||||
|
|
||||||
|
def tg_bot_start():
|
||||||
|
"""Start the telegram bot."""
|
||||||
|
# FB - Create the Updater
|
||||||
|
updater = Updater(os.environ.get('TELEGRAM_BOT_TOKEN'), use_context=True)
|
||||||
|
|
||||||
|
# FB - Get the dispatcher to register handlers
|
||||||
|
dispatcher = updater.dispatcher
|
||||||
|
|
||||||
|
# FB - on /help command
|
||||||
|
dispatcher.add_handler(CommandHandler("help", tg_help_handler))
|
||||||
|
|
||||||
|
# FB - log all errors
|
||||||
|
dispatcher.add_error_handler(tg_error_handler)
|
||||||
|
|
||||||
|
# FB - Start the Bot
|
||||||
|
updater.start_polling()
|
||||||
|
|
||||||
|
return updater.bot
|
||||||
|
|
||||||
|
|
||||||
|
def tg_send_owner_message(message):
|
||||||
|
"""Send telegram message to owner."""
|
||||||
|
telegram_bot.sendMessage(text=message, chat_id=os.environ.get('TELEGRAM_OWNER'), parse_mode=ParseMode.MARKDOWN)
|
||||||
|
|
||||||
|
|
||||||
|
def tg_send_subscribers_message(message):
|
||||||
|
"""Send telegram messages to subscribers."""
|
||||||
|
for telegram_destination in os.environ.get('TELEGRAM_SUBSCRIBERS').split(','):
|
||||||
|
telegram_bot.sendMessage(
|
||||||
|
text=message, chat_id=telegram_destination, parse_mode=ParseMode.MARKDOWN)
|
||||||
|
|
||||||
|
|
||||||
|
@webhook_listener.route('/', methods=['GET'])
|
||||||
|
def index():
|
||||||
|
"""Upon call of homepage."""
|
||||||
|
logger.info('"/" reached, IP: %s', request.remote_addr)
|
||||||
|
|
||||||
|
return webhook_listener.send_static_file('Index.html')
|
||||||
|
|
||||||
|
|
||||||
|
@webhook_listener.route('/message', methods=['POST'])
|
||||||
|
@validate_twilio_request
|
||||||
|
def recv_message():
|
||||||
|
"""Upon reception of a SMS."""
|
||||||
|
logger.info(' "/message" reached, IP: %s', request.remote_addr)
|
||||||
|
# FB - Format telegram Message
|
||||||
|
telegram_message = 'Text from `{From}` ({Country}, {State}) :``` {Body}```'.format(
|
||||||
|
From=request.values.get('From', 'unknown'),
|
||||||
|
Country=request.values.get('FromCountry', 'unknown'),
|
||||||
|
State=request.values.get('FromState', 'unknown'),
|
||||||
|
Body=request.values.get('Body', 'unknown')
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(telegram_message)
|
||||||
|
# FB - Send telegram alerts
|
||||||
|
tg_send_owner_message('Twilio ID : `{Id}`\n'.format(
|
||||||
|
Id=request.values.get('MessageSid', 'unknown')) + telegram_message)
|
||||||
|
tg_send_subscribers_message(telegram_message)
|
||||||
|
|
||||||
|
# FB - return empty response to avoid further Twilio fees
|
||||||
|
return '<response></response>'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@webhook_listener.route('/call', methods=['POST'])
|
||||||
|
@validate_twilio_request
|
||||||
|
def recv_call():
|
||||||
|
"""Upon reception of a call."""
|
||||||
|
logger.info(' "/call" reached, IP: %s', request.remote_addr)
|
||||||
|
# FB - Format telegram Message
|
||||||
|
telegram_message = 'Call from `{From}` ({Country}, {State}) :``` {Status}```'.format(
|
||||||
|
From=request.values.get('From', 'unknown'),
|
||||||
|
Country=request.values.get('FromCountry', 'unknown'),
|
||||||
|
State=request.values.get('FromState', 'unknown'),
|
||||||
|
Status=request.values.get('CallStatus', 'unknown')
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(telegram_message)
|
||||||
|
# FB - Send telegram alerts
|
||||||
|
tg_send_owner_message('Twilio ID : `{Id}`\n'.format(
|
||||||
|
Id=request.values.get('CallSid', 'unknown')) + telegram_message)
|
||||||
|
tg_send_subscribers_message(telegram_message)
|
||||||
|
|
||||||
|
# FB - reject the call without being billed
|
||||||
|
return '<Response><Reject/></Response>'
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logger.info('Starting bot')
|
||||||
|
# FB - Start the telegram bot
|
||||||
|
telegram_bot = tg_bot_start()
|
||||||
|
# FB - Start the website
|
||||||
|
serve(webhook_listener, host='0.0.0.0', port=80)
|
||||||
Reference in New Issue
Block a user