Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2aaf419
Update front sub module
AntonZelinsky May 14, 2023
c9fbbef
Add user's Telegram ID to start message
Legyan May 15, 2023
ed1a994
Add button to start to messsage
May 15, 2023
4d96e4d
added endpoint api/v1/send_telegram_message
Legyan May 16, 2023
082366e
fix typo
Legyan May 17, 2023
03a234d
fix isort
May 18, 2023
2da260f
add extra \n
Legyan May 19, 2023
c19327e
change telegram_id from body to path parameters
Legyan May 19, 2023
64fbeb0
fixed the "buttons" variable
May 19, 2023
76eefae
Merge pull request #99 from ProCharity/feature/add_telegram_id_to_sta…
AleksandrLyo May 20, 2023
13ad2c1
Merge pull request #100 from ProCharity/feature/add_button_to_url_to_…
AleksandrLyo May 20, 2023
9439cbe
fix url
May 20, 2023
132caa9
move has_mailing from body to path parameters
Legyan May 21, 2023
912ced6
remove "has_mailing" parameter, add "mode" to body
Legyan May 23, 2023
7fe2c20
small refactor
Legyan May 25, 2023
060347a
Merge pull request #102 from ProCharity/refactor/send_telegram_notifi…
AntonZelinsky Jun 22, 2023
7eb162a
added custom Exeption class to send_tg_message_to_user.py
Jun 28, 2023
5289825
small refactoring
Jun 28, 2023
fc7fde3
add db rollback at InvalidAPIUsage exception.
Jun 29, 2023
4925065
add docstrings to InvalidAPIUsage class and handler.
Jun 29, 2023
bf207cd
Merge branch 'develop' into feature/endpoint_send_tg_message_to_user
AleksandrLyo Jun 29, 2023
9b2c08b
Merge pull request #101 from ProCharity/feature/endpoint_send_tg_mess…
AleksandrLyo Jun 29, 2023
d6a95a4
fix token
AleksandrLyo Jul 3, 2023
18b7748
sent_by set nullable
AleksandrLyo Jul 3, 2023
8a57e70
create migration
AleksandrLyo Jul 3, 2023
c175e64
Merge branch 'feature/endpoint_send_tg_message_to_user' of github.com…
AleksandrLyo Jul 3, 2023
df7c172
Merge pull request #104 from ProCharity/feature/endpoint_send_tg_mess…
AntonZelinsky Jul 3, 2023
0a1d431
Delete required for message param
AleksandrLyo Jul 3, 2023
85fb378
Merge pull request #105 from ProCharity/feature/endpoint_send_tg_mess…
AleksandrLyo Jul 3, 2023
a0f46b1
fix url in button link account with bot
AleksandrLyo Jul 3, 2023
e223c73
delete tg id in start message, add URL_PROCHARITY to develop ci
AleksandrLyo Jul 7, 2023
946d8a2
add URL_PROCHARITY to bot config
AleksandrLyo Jul 7, 2023
18a5afb
Merge branch 'develop' into feature/add_button_to_url_to_start_message
AleksandrLyo Jul 7, 2023
5d98421
add URL_PROCHARITY to master ci
AleksandrLyo Jul 7, 2023
f956388
Merge pull request #106 from ProCharity/feature/add_button_to_url_to_…
AntonZelinsky Jul 7, 2023
abd15a8
fix start command
AleksandrLyo Jul 8, 2023
3ef35ad
Merge pull request #107 from ProCharity/feature/add_button_to_url_to_…
AleksandrLyo Jul 8, 2023
dd81c67
add user_id to ExternalUserRegistration
AleksandrLyo Jul 10, 2023
0d023d0
Merge pull request #108 from ProCharity/fix/external_user_registration
AntonZelinsky Jul 10, 2023
df12c33
fix external_user_registration and change logging level to DEBUG
AleksandrLyo Jul 26, 2023
2cf4b12
Merge pull request #109 from ProCharity/fix/external_user_registration
AntonZelinsky Jul 26, 2023
5ac29e4
fix ExternalUserRegistration json kwargs
AleksandrLyo Aug 3, 2023
0333430
Merge pull request #110 from ProCharity/fix/external_user_registration
AleksandrLyo Aug 3, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ MAIL_PORT=587
MAIL_USE_TLS=True
MAIL_USERNAME=
MAIL_DEFAULT_SENDER=
URL_PROCHARITY=
EMAIL_PROCHARRITY=
HOST_NAME=
ACCESS_TOKEN_FOR_PROCHARITY=
Expand Down
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ MAIL_PORT=
MAIL_USE_TLS=
MAIL_USERNAME=
MAIL_DEFAULT_SENDER=
URL_PROCHARITY=
EMAIL_PROCHARRITY=
DATABASE_URL=
HOST_NAME=
Expand Down
1 change: 1 addition & 0 deletions .env.prod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ MAIL_PORT=
MAIL_USE_TLS=
MAIL_USERNAME=
MAIL_DEFAULT_SENDER=
URL_PROCHARITY=
EMAIL_PROCHARRITY=
DATABASE_URL=
HOST_NAME=
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/develop_bot_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ jobs:
echo MAIL_USE_TLS=${{ secrets.MAIL_USE_TLS }} >> .env
echo MAIL_USERNAME=${{ secrets.MAIL_USERNAME }} >> .env
echo MAIL_DEFAULT_SENDER=${{ secrets.MAIL_DEFAULT_SENDER }} >> .env
echo URL_PROCHARITY=${{ secrets.URL_PROCHARITY }} >> .env
echo EMAIL_PROCHARRITY=${{ secrets.EMAIL_PROCHARRITY }} >> .env
echo HOST_NAME=${{ secrets.HOST_NAME }} >> .env
echo ACCESS_TOKEN_FOR_PROCHARITY=${{ secrets.ACCESS_TOKEN_FOR_PROCHARITY }} >> .env
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/master_bot_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ jobs:
echo MAIL_USE_TLS=${{ secrets.MAIL_USE_TLS }} >> .env
echo MAIL_USERNAME=${{ secrets.MAIL_USERNAME }} >> .env
echo MAIL_DEFAULT_SENDER=${{ secrets.MAIL_DEFAULT_SENDER }} >> .env
echo URL_PROCHARITY=${{ secrets.URL_PROCHARITY }} >> .env
echo EMAIL_PROCHARRITY=${{ secrets.EMAIL_PROCHARRITY }} >> .env
echo HOST_NAME=${{ secrets.HOST_NAME_PROD }} >> .env
echo ACCESS_TOKEN_FOR_PROCHARITY=${{ secrets.ACCESS_TOKEN_FOR_PROCHARITY_PROD }} >> .env
Expand Down
3 changes: 3 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from app import config
from app.config import TELEGRAM_TOKEN


jwt = JWTManager()
mail = Mail()
cors = CORS()
Expand All @@ -26,12 +27,14 @@ def create_app():
from app.webhooks import swagger_webhooks

from app.auth import auth_bp
from app.error_handlers import invalid_api_usage, InvalidAPIUsage
from app.front import front_bp
from app.webhooks import webhooks_bp

app.register_blueprint(webhooks_bp)
app.register_blueprint(auth_bp)
app.register_blueprint(front_bp)
app.register_error_handler(InvalidAPIUsage, invalid_api_usage)

jwt.init_app(app)
mail.init_app(app)
Expand Down
6 changes: 3 additions & 3 deletions app/auth/external_users_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ class ExternalUserRegistration(MethodResource, Resource):
}
)
@use_kwargs(
{'id': fields.Int(required=True),
{'user_id': fields.Int(required=True),
'id_hash': fields.Str(description='md5 hash of external_id', required=True),
'first_name': fields.Str(required=True),
'last_name': fields.Str(required=True),
'email': fields.Str(required=True),
'specializations': fields.Str(required=True)}
)
def post(self, **kwargs):
external_id = kwargs.get('id')
external_id = kwargs.get('user_id')

user = ExternalSiteUser.query.options(load_only('external_id')).filter_by(external_id=external_id).first()
if user:
Expand All @@ -63,7 +63,7 @@ def post(self, **kwargs):
categories = []

for specialization in specializations:
user.categories.append(specialization.name)
categories.append(specialization.name)

try:
db_session.commit()
Expand Down
2 changes: 2 additions & 0 deletions app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
'lowercase': 1,
'max_length': 32,
}

URL_PROCHARITY = os.getenv('URL_PROCHARITY')
# procharity send email settings
PROCHARRITY_TEMPLATE = 'email_templates/send_question.html'

Expand Down
20 changes: 20 additions & 0 deletions app/error_handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class InvalidAPIUsage(Exception):
"""
Special class for custom Exceptions raised where it is necessary.
If Exception is not explicitly defined raises HTTP 400 Bad Request.
Error Message is required to clarify erroneous program behavior.
"""
status_code = 400

def __init__(self, message, status_code=None):
super().__init__()
self.message = message
if status_code is not None:
self.status_code = status_code


def invalid_api_usage(error):
"""
InvalidAPIUsage handler function.
"""
return error.message, error.status_code
5 changes: 4 additions & 1 deletion app/front/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@

from . import analytics
from . import send_tg_notification
from . import send_tg_message_to_user
from . import users
from . import download_log_files

front_api.add_resource(analytics.Analytics, '/api/v1/analytics/')
front_api.add_resource(send_tg_notification.SendTelegramNotification,
'/api/v1/send_telegram_notification/')
'/api/v1/messages/')
front_api.add_resource(send_tg_message_to_user.SendTelegramMessage,
'/api/v1/messages/<int:telegram_id>/')
front_api.add_resource(users.UsersList, '/api/v1/users/')
front_api.add_resource(users.UserItem, '/api/v1/users/<int:telegram_id>/')
front_api.add_resource(download_log_files.DownloadLogs, '/api/v1/download_logs/')
Expand Down
97 changes: 97 additions & 0 deletions app/front/send_tg_message_to_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import datetime

from flask import jsonify, make_response
from flask_apispec import doc, use_kwargs
from flask_apispec.views import MethodResource
from flask_restful import Resource
from marshmallow import Schema, fields
from sqlalchemy.exc import SQLAlchemyError

from app.database import db_session
from app.error_handlers import InvalidAPIUsage
from app.logger import app_logger as logger
from app.models import Notification
from app.webhooks.check_webhooks_token import check_webhooks_token
from bot.messages import TelegramMessage


class TelegramMessageSchema(Schema):
message = fields.String(required=True)


class SendTelegramMessage(Resource, MethodResource):
method_decorators = {'post': [check_webhooks_token]}

@doc(description='Sends message to the telegram user.'
'Requires "message" and "telegram_id" parameters.',
summary='Send telegram messages to the bot chat',
tags=['Messages'],
responses={
200: {'description': 'The message has been sent'},
400: {'description': 'The message can not be empty'},
},
params={
'message': {
'description': 'Message to user. Max len 4096',
'in': 'query',
'type': 'string'
},
'telegram_id': {
'description': (
'Sending notification to user with this telegram id'
),
'in': 'path',
'type': 'integer',
'required': True
},
'token': {
'description': 'webhooks token',
'in': 'header',
'type': 'string',
'required': True
}
})
@use_kwargs(TelegramMessageSchema)
def post(self, telegram_id, **kwargs):
message = kwargs.get('message').replace('&nbsp;', '')

if not message:
logger.info(
'Messages: The <message> parameter have not been passed'
)
return make_response(
jsonify(
result=(
'Необходимо указать параметр <message>.'
)
), 400
)

message = Notification(message=message)
db_session.add(message)
try:
db_session.commit()
mes = TelegramMessage(telegram_id)
mes.send_message(message=message.message)
message.was_sent = True
message.sent_date = datetime.datetime.now()
db_session.commit()
except InvalidAPIUsage as ex:
db_session.rollback()
return make_response(
jsonify(result=ex.message), ex.status_code
)
except SQLAlchemyError as ex:
logger.error(f'Messages: Database commit error "{str(ex)}"')
db_session.rollback()
return make_response(
jsonify(message=f'Bad request: {str(ex)}'), 400
)

logger.info(f'Messages: The message "{message.message}" '
'has been successfully sent to user')
return make_response(
jsonify(
result="Сообщение успешно отправлено пользователю."
), 200
)
66 changes: 46 additions & 20 deletions app/front/send_tg_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@
from bot.messages import TelegramNotification



class TelegramNotificationSchema(Schema):
message = fields.String(required=True)
has_mailing = fields.String(required=True)
mode = fields.String(required=True)


class SendTelegramNotification(Resource, MethodResource):

@doc(description='Sends message to the Telegram chat. Requires "message" parameter.'
' Messages can be sent either to subscribed users or not.To do this,'
' specify the "has_mailing" parameter.Default value "True".',
@doc(description=(
'Sends message to the Telegram chat. Requires "message" parameter.'
' Messages can be sent either to subscribed users or not.To do '
'this, specify the "mode" parameter.Default value "subscribed".'
),
summary='Send messages to the bot chat',
tags=['Messages'],
responses={
Expand All @@ -40,39 +41,57 @@ class SendTelegramNotification(Resource, MethodResource):
'type': 'string',
'required': True
},
'has_mailing': {
'description': ('Sending notifications to users by the type of permission to mailing.'
'mode': {
'description': ('Sending notifications to users by the type '
'of permission to mailing.'
'subscribed - user has enabled a mailing.'
'unsubscribed - user has disabled a mailing.'
'all - send to all users'),
'in': 'query',
'type': 'string',
'required': True
},
'Authorization': config.PARAM_HEADER_AUTH, # Only if request requires authorization
# Only if request requires authorization
'Authorization': config.PARAM_HEADER_AUTH,
}
)
@use_kwargs(TelegramNotificationSchema)
@jwt_required()
def post(self, **kwargs):
message = kwargs.get('message').replace('&nbsp;', '')
has_mailing = kwargs.get('has_mailing')
mode = kwargs.get('mode')

if not message or not has_mailing:
logger.info("Messages: The <message> and <has_mailing> parameters have not been passed")
return make_response(jsonify(result="Необходимо указать параметры <message> и <has_mailing>."), 400)
if not message or not mode:
logger.info(
'Messages: The <message> and <mode> '
'parameters have not been passed'
)
return make_response(
jsonify(
result='Необходимо указать параметры <message> и <mode>.'
),
400
)

authorized_user = get_jwt_identity()
message = Notification(message=message, sent_by=authorized_user)
db_session.add(message)
try:
db_session.commit()
job_queue = TelegramNotification(has_mailing)
job_queue = TelegramNotification(mode)

if not job_queue.send_notification(message=message.message):
logger.info(f"Messages: Passed invalid <has_mailing> parameter. Passed: {has_mailing}")
return make_response(jsonify(result=f"Неверно указан параметр <has_mailing>. "
f"Сообщение не отправлено."), 400)
logger.info(
'Messages: Passed invalid <mode> parameter. '
f'Passed: {mode}'
)
return make_response(
jsonify(
result=('Неверно указан параметр <mode>. '
'Сообщение не отправлено.')
),
400
)

message.was_sent = True
message.sent_date = datetime.datetime.now()
Expand All @@ -81,8 +100,15 @@ def post(self, **kwargs):
except SQLAlchemyError as ex:
logger.error(f'Messages: Database commit error "{str(ex)}"')
db_session.rollback()
return make_response(jsonify(message=f'Bad request: {str(ex)}'), 400)
return make_response(
jsonify(message=f'Bad request: {str(ex)}'), 400
)

logger.info(f"Messages: The message '{message.message[0:30]}...' "
f"has been successfully added to the mailing list.")
return make_response(jsonify(result=f"Сообщение успешно добавлено в очередь рассылки."), 200)
logger.info(f'Messages: The message "{message.message[0:30]}..." '
f'has been successfully added to the mailing list.')
return make_response(
jsonify(
result='Сообщение успешно добавлено в очередь рассылки.'
),
200
)
2 changes: 2 additions & 0 deletions app/front/swagger_front.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
from app.front.download_log_files import DownloadLogs, GetListLogFiles
from app.front.users import UsersList, UserItem
from app.front.send_tg_notification import SendTelegramNotification
from app.front.send_tg_message_to_user import SendTelegramMessage


docs.register(Analytics, blueprint='front_bp')
docs.register(SendTelegramNotification, blueprint='front_bp')
docs.register(SendTelegramMessage, blueprint='front_bp')
docs.register(UsersList, blueprint='front_bp')
docs.register(UserItem, blueprint='front_bp')
docs.register(DownloadLogs, blueprint='front_bp')
Expand Down
6 changes: 3 additions & 3 deletions app/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def add_handler(path_name):

def app_logging():
module_logger = logging.getLogger('app')
module_logger.setLevel(logging.INFO)
module_logger.setLevel(logging.DEBUG)
app_handler = add_handler('app_logs.txt')
app_loggers = [
module_logger,
Expand All @@ -47,7 +47,7 @@ def app_logging():

def bot_logging():
bot_logger = logging.getLogger("telegram")
bot_logger.setLevel(logging.INFO)
bot_logger.setLevel(logging.DEBUG)
bot_handler = add_handler('bot_logs.txt')
bot_logger.addHandler(bot_handler)
app_loggers = [
Expand All @@ -63,7 +63,7 @@ def bot_logging():

def webhooks_logging():
webhooks_logger = logging.getLogger("webhooks")
webhooks_logger.setLevel(logging.INFO)
webhooks_logger.setLevel(logging.DEBUG)
webhooks_handler = add_handler('webhooks_logs.txt')
webhooks_logger.addHandler(webhooks_handler)

Expand Down
2 changes: 1 addition & 1 deletion app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class Notification(Base):
message = Column(String(4096), nullable=False)
was_sent = Column(Boolean, default=False)
sent_date = Column(TIMESTAMP)
sent_by = Column(String(48), nullable=False)
sent_by = Column(String(48), nullable=True)

def __repr__(self):
return f'<Notification {self.message[0:10]}>'
Expand Down
Loading