Skip to content

Commit

Permalink
Merge pull request #47 from ohld/separate-tgbot-and-django-app
Browse files Browse the repository at this point in the history
Separate tgbot and django app
  • Loading branch information
shamaevnn authored Sep 15, 2022
2 parents dc7a4b9 + ea714a3 commit 424a8d8
Show file tree
Hide file tree
Showing 30 changed files with 163 additions and 151 deletions.
4 changes: 2 additions & 2 deletions arcgis/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
('tgbot', '0001_initial'),
('users', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='Arcgis',
fields=[
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
('location', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='tgbot.location')),
('location', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='users.location')),
('match_addr', models.CharField(max_length=200)),
('long_label', models.CharField(max_length=200)),
('short_label', models.CharField(max_length=128)),
Expand Down
2 changes: 1 addition & 1 deletion arcgis/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.db import models

# Create your models here.
from tgbot.models import Location
from users.models import Location
from utils.models import CreateTracker, GetOrNoneManager


Expand Down
2 changes: 1 addition & 1 deletion dtb/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
'debug_toolbar',

# local apps
'tgbot.apps.TgbotConfig',
'users.apps.UsersConfig',
'arcgis',
]

Expand Down
6 changes: 5 additions & 1 deletion dtb/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
import debug_toolbar
from django.contrib import admin
from django.urls import path, include
from django.views.decorators.csrf import csrf_exempt

from . import views

urlpatterns = [
path('tgadmin/', admin.site.urls),
path('', include('tgbot.urls')),
path('__debug__/', include(debug_toolbar.urls)),
path('', views.index, name="index"),
path('super_secter_webhook/', csrf_exempt(views.TelegramBotWebhookView.as_view())),
]
23 changes: 15 additions & 8 deletions tgbot/views.py → dtb/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,40 @@
import logging
from django.views import View
from django.http import JsonResponse
from telegram import Update

from dtb.celery import app
from dtb.settings import DEBUG
from tgbot.dispatcher import process_telegram_event
from tgbot.dispatcher import dispatcher
from tgbot.main import bot

logger = logging.getLogger(__name__)


@app.task(ignore_result=True)
def process_telegram_event(update_json):
update = Update.de_json(update_json, bot)
dispatcher.process_update(update)


def index(request):
return JsonResponse({"error": "sup hacker"})


class TelegramBotWebhookView(View):
# WARNING: if fail - Telegram webhook will be delivered again.
# WARNING: if fail - Telegram webhook will be delivered again.
# Can be fixed with async celery task execution
def post(self, request, *args, **kwargs):
if DEBUG:
process_telegram_event(json.loads(request.body))
else:
else:
# Process Telegram event in Celery worker (async)
# Don't forget to run it and & Redis (message broker for Celery)!
# Read Procfile for details
# You can run all of these services via docker-compose.yml
# Don't forget to run it and & Redis (message broker for Celery)!
# Locally, You can run all of these services via docker-compose.yml
process_telegram_event.delay(json.loads(request.body))

# TODO: there is a great trick to send action in webhook response
# e.g. remove buttons, typing event
return JsonResponse({"ok": "POST request processed"})

def get(self, request, *args, **kwargs): # for debug
return JsonResponse({"ok": "Get request received! But nothing done"})
26 changes: 25 additions & 1 deletion run_polling.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,31 @@
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dtb.settings')
django.setup()

from tgbot.dispatcher import run_polling
from telegram import Bot
from telegram.ext import Updater

from dtb.settings import TELEGRAM_TOKEN
from tgbot.dispatcher import setup_dispatcher


def run_polling(tg_token: str = TELEGRAM_TOKEN):
""" Run bot in polling mode """
updater = Updater(tg_token, use_context=True)

dp = updater.dispatcher
dp = setup_dispatcher(dp)

bot_info = Bot(tg_token).get_me()
bot_link = f"https://t.me/{bot_info['username']}"

print(f"Polling of '{bot_link}' has started")
# it is really useful to send '👋' emoji to developer
# when you run local test
# bot.send_message(text='👋', chat_id=<YOUR TELEGRAM ID>)

updater.start_polling()
updater.idle()


if __name__ == "__main__":
run_polling()
5 changes: 0 additions & 5 deletions tgbot/apps.py

This file was deleted.

102 changes: 6 additions & 96 deletions tgbot/dispatcher.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
"""
Telegram event handlers
"""
import sys
import logging
from typing import Dict

import telegram.error
from telegram import Bot, Update, BotCommand
from telegram.ext import (
Updater, Dispatcher, Filters,
Dispatcher, Filters,
CommandHandler, MessageHandler,
CallbackQueryHandler,
)

from dtb.celery import app # event processing in async mode
from dtb.settings import TELEGRAM_TOKEN, DEBUG
from dtb.settings import DEBUG
from tgbot.handlers.broadcast_message.manage_data import CONFIRM_DECLINE_BROADCAST
from tgbot.handlers.broadcast_message.static_text import broadcast_command
from tgbot.handlers.onboarding.manage_data import SECRET_LEVEL_BUTTON

from tgbot.handlers.utils import files, error
from tgbot.handlers.admin import handlers as admin_handlers
from tgbot.handlers.location import handlers as location_handlers
from tgbot.handlers.onboarding import handlers as onboarding_handlers
from tgbot.handlers.broadcast_message import handlers as broadcast_handlers
from tgbot.handlers.onboarding.manage_data import SECRET_LEVEL_BUTTON
from tgbot.handlers.broadcast_message.manage_data import CONFIRM_DECLINE_BROADCAST
from tgbot.handlers.broadcast_message.static_text import broadcast_command
from tgbot.main import bot


def setup_dispatcher(dp):
Expand Down Expand Up @@ -76,89 +70,5 @@ def setup_dispatcher(dp):
return dp


def run_polling():
""" Run bot in polling mode """
updater = Updater(TELEGRAM_TOKEN, use_context=True)

dp = updater.dispatcher
dp = setup_dispatcher(dp)

bot_info = Bot(TELEGRAM_TOKEN).get_me()
bot_link = f"https://t.me/" + bot_info["username"]

print(f"Polling of '{bot_link}' has started")
# it is really useful to send '👋' emoji to developer
# when you run local test
# bot.send_message(text='👋', chat_id=<YOUR TELEGRAM ID>)

updater.start_polling()
updater.idle()


# Global variable - best way I found to init Telegram bot
bot = Bot(TELEGRAM_TOKEN)
try:
TELEGRAM_BOT_USERNAME = bot.get_me()["username"]
except telegram.error.Unauthorized:
logging.error(f"Invalid TELEGRAM_TOKEN.")
sys.exit(1)


@app.task(ignore_result=True)
def process_telegram_event(update_json):
update = Update.de_json(update_json, bot)
dispatcher.process_update(update)


def set_up_commands(bot_instance: Bot) -> None:
langs_with_commands: Dict[str, Dict[str, str]] = {
'en': {
'start': 'Start django bot 🚀',
'stats': 'Statistics of bot 📊',
'admin': 'Show admin info ℹ️',
'ask_location': 'Send location 📍',
'broadcast': 'Broadcast message 📨',
'export_users': 'Export users.csv 👥',
},
'es': {
'start': 'Iniciar el bot de django 🚀',
'stats': 'Estadísticas de bot 📊',
'admin': 'Mostrar información de administrador ℹ️',
'ask_location': 'Enviar ubicación 📍',
'broadcast': 'Mensaje de difusión 📨',
'export_users': 'Exportar users.csv 👥',
},
'fr': {
'start': 'Démarrer le bot Django 🚀',
'stats': 'Statistiques du bot 📊',
'admin': "Afficher les informations d'administrateur ℹ️",
'ask_location': 'Envoyer emplacement 📍',
'broadcast': 'Message de diffusion 📨',
"export_users": 'Exporter users.csv 👥',
},
'ru': {
'start': 'Запустить django бота 🚀',
'stats': 'Статистика бота 📊',
'admin': 'Показать информацию для админов ℹ️',
'broadcast': 'Отправить сообщение 📨',
'ask_location': 'Отправить локацию 📍',
'export_users': 'Экспорт users.csv 👥',
}
}

bot_instance.delete_my_commands()
for language_code in langs_with_commands:
bot_instance.set_my_commands(
language_code=language_code,
commands=[
BotCommand(command, description) for command, description in langs_with_commands[language_code].items()
]
)


# WARNING: it's better to comment the line below in DEBUG mode.
# Likely, you'll get a flood limit control error, when restarting bot too often
set_up_commands(bot)

n_workers = 0 if DEBUG else 4
dispatcher = setup_dispatcher(Dispatcher(bot, update_queue=None, workers=n_workers, use_context=True))
4 changes: 3 additions & 1 deletion tgbot/handlers/admin/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

from tgbot.handlers.admin import static_text
from tgbot.handlers.admin.utils import _get_csv_from_qs_values
from tgbot.models import User
from tgbot.handlers.utils.info import send_typing_action
from users.models import User


def admin(update: Update, context: CallbackContext) -> None:
Expand Down Expand Up @@ -37,6 +38,7 @@ def stats(update: Update, context: CallbackContext) -> None:
)


@send_typing_action
def export_users(update: Update, context: CallbackContext) -> None:
u = User.get_user(update, context)
if not u.is_admin:
Expand Down
4 changes: 2 additions & 2 deletions tgbot/handlers/broadcast_message/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from .keyboards import keyboard_confirm_decline_broadcasting
from .static_text import broadcast_command, broadcast_wrong_format, broadcast_no_access, error_with_html, \
message_is_sent, declined_message_broadcasting
from tgbot.models import User
from tgbot.tasks import broadcast_message
from users.models import User
from users.tasks import broadcast_message


def broadcast_command_with_message(update: Update, context: CallbackContext):
Expand Down
8 changes: 4 additions & 4 deletions tgbot/handlers/broadcast_message/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
from telegram import MessageEntity, InlineKeyboardButton, InlineKeyboardMarkup

from dtb.settings import TELEGRAM_TOKEN
from tgbot.models import User
from users.models import User


def _from_celery_markup_to_markup(celery_markup: Optional[List[List[Dict]]]) -> Optional[InlineKeyboardMarkup]:
def from_celery_markup_to_markup(celery_markup: Optional[List[List[Dict]]]) -> Optional[InlineKeyboardMarkup]:
markup = None
if celery_markup:
markup = []
Expand All @@ -26,7 +26,7 @@ def _from_celery_markup_to_markup(celery_markup: Optional[List[List[Dict]]]) ->
return markup


def _from_celery_entities_to_entities(celery_entities: Optional[List[Dict]] = None) -> Optional[List[MessageEntity]]:
def from_celery_entities_to_entities(celery_entities: Optional[List[Dict]] = None) -> Optional[List[MessageEntity]]:
entities = None
if celery_entities:
entities = [
Expand All @@ -42,7 +42,7 @@ def _from_celery_entities_to_entities(celery_entities: Optional[List[Dict]] = No
return entities


def _send_message(
def send_one_message(
user_id: Union[str, int],
text: str,
parse_mode: Optional[str] = telegram.ParseMode.HTML,
Expand Down
2 changes: 1 addition & 1 deletion tgbot/handlers/location/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from tgbot.handlers.location.static_text import share_location, thanks_for_location
from tgbot.handlers.location.keyboards import send_location_keyboard
from tgbot.models import User, Location
from users.models import User, Location


def ask_for_location(update: Update, context: CallbackContext) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tgbot/handlers/onboarding/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from tgbot.handlers.onboarding import static_text
from tgbot.handlers.utils.info import extract_user_data_from_update
from tgbot.models import User
from users.models import User
from tgbot.handlers.onboarding.keyboards import make_keyboard_for_start_command


Expand Down
2 changes: 1 addition & 1 deletion tgbot/handlers/utils/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from telegram.ext import CallbackContext

from dtb.settings import TELEGRAM_LOGS_CHAT_ID
from tgbot.models import User
from users.models import User


def send_stacktrace_to_tg_chat(update: Update, context: CallbackContext) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tgbot/handlers/utils/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from telegram import Update
from telegram.ext import CallbackContext

from tgbot.models import User
from users.models import User

ALL_TG_FILE_TYPES = ["document", "video_note", "voice", "sticker", "audio", "video", "animation", "photo"]

Expand Down
5 changes: 4 additions & 1 deletion tgbot/handlers/utils/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
import telegram
from telegram import Update

from tgbot.main import bot


def send_typing_action(func: Callable):
"""Sends typing action while processing func command."""
@wraps(func)
def command_func(update, context, *args, **kwargs):
context.bot.send_chat_action(chat_id=update.effective_message.chat_id, action=telegram.ChatAction.TYPING)
bot.send_chat_action(
chat_id=update.effective_message.chat_id, action=telegram.ChatAction.TYPING)
return func(update, context, *args, **kwargs)

return command_func
Expand Down
17 changes: 17 additions & 0 deletions tgbot/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import logging
import sys

import telegram
from telegram import Bot

from dtb.settings import TELEGRAM_TOKEN


bot = Bot(TELEGRAM_TOKEN)
TELEGRAM_BOT_USERNAME = bot.get_me()["username"]
# Global variable - the best way I found to init Telegram bot
try:
pass
except telegram.error.Unauthorized:
logging.error("Invalid TELEGRAM_TOKEN.")
sys.exit(1)
Loading

0 comments on commit 424a8d8

Please sign in to comment.