mirror of
https://github.com/nikdoof/smsbot.git
synced 2025-12-11 09:02:16 +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