Skip to content

Commit

Permalink
Organize code and add new DISABLE_SETUP configuration option (#9)
Browse files Browse the repository at this point in the history
* Add DISABLE_SETUP option to configuration

* Change structure of storing bots internally

* Use BotData class instead of dict for storing data
  • Loading branch information
HosseyNJF authored Jul 20, 2020
1 parent 5f07ec5 commit 0e76b6c
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 90 deletions.
11 changes: 9 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ django-telegrambot

.. image:: https://img.shields.io/badge/Donate-PayPal-green.svg
:target: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=LMXQVQ3YA2JJQ

.. image:: http://pepy.tech/badge/django-telegrambot
:target: http://pepy.tech/count/django-telegrambot

Expand Down Expand Up @@ -77,10 +77,16 @@ And set your bots::
# apps contain telegrambot.py files that cannot be successfully
# imported.

'DISABLE_SETUP': False, # If set to True, there will be no tries to set webhook or read
# updates from the telegram server on app's start
# (useful when developing on local machine; makes django's startup faster)

'BOT_MODULE_NAME': 'telegrambot_handlers', #(Optional [str]) # The default name for file name containing telegram handlers which has to be placed inside your local app(s). Default is 'telegrambot'. Example is to put "telegrambot_handlers.py" file to local app's folder.

'BOTS' : [
{
'ID': 'MainBot', #Unique identifier for your bot (used in your code only)

'TOKEN': '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', #Your bot token.

#'CONTEXT': True, # Use context based handler functions
Expand All @@ -104,7 +110,7 @@ And set your bots::
#delivery, 1-100. Defaults to 40. Use lower values to limit the
#load on your bot's server, and higher values to increase your
#bot's throughput.

# 'MESSAGEQUEUE_ENABLED':(Optinal[bool]), # Make this True if you want to use messagequeue

# 'MESSAGEQUEUE_ALL_BURST_LIMIT':(Optional[int]), # If not provided 29 is the default value
Expand Down Expand Up @@ -180,6 +186,7 @@ Then use it in a project creating a module ``telegrambot.py`` in your app ::
# Default dispatcher (this is related to the first bot in settings.DJANGO_TELEGRAMBOT['BOTS'])
dp = DjangoTelegramBot.dispatcher
# To get Dispatcher related to a specific bot
# dp = DjangoTelegramBot.getDispatcher('BOT_n_id') #get by bot identifier
# dp = DjangoTelegramBot.getDispatcher('BOT_n_token') #get by bot token
# dp = DjangoTelegramBot.getDispatcher('BOT_n_username') #get by bot username

Expand Down
184 changes: 98 additions & 86 deletions django_telegrambot/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
# django_telegram_bot/apps.py
import os.path
import importlib
from collections import OrderedDict
from typing import List

import telegram
import logging
from time import sleep
Expand All @@ -19,6 +22,7 @@
from telegram.utils.request import Request
from telegram.ext import messagequeue as mq

from .bot import BotData
from .mqbot import MQBot


Expand All @@ -43,42 +47,61 @@ class DjangoTelegramBot(AppConfig):
name = 'django_telegrambot'
verbose_name = 'Django TelegramBot'
ready_run = False
bot_tokens = []
bot_usernames = []
dispatchers = []
bots = []
updaters = []
bots_data: List[BotData] = list()
__used_tokens = set()

@classproperty
def dispatcher(cls):
#print("Getting value default dispatcher")
cls.__used_tokens.add(cls.bot_tokens[0])
return cls.dispatchers[0]
try:
#print("Getting value default dispatcher")
bot_data = cls.bots_data[0]
cls.__used_tokens.add(bot_data.token)
return bot_data.dispatcher
except (StopIteration, IndexError):
raise ReferenceError("No bots are defined")

@classproperty
def updater(cls):
#print("Getting value default updater")
cls.__used_tokens.add(cls.bot_tokens[0])
return cls.updaters[0]
try:
#print("Getting value default dispatcher")
bot_data = cls.bots_data[0]
cls.__used_tokens.add(bot_data.token)
return bot_data.updater
except (StopIteration, IndexError):
raise ReferenceError("No bots are defined")


@classmethod
def get_dispatcher(cls, bot_id=None, safe=True):
def _get_bot_by_id(cls, bot_id=None, safe=True):
if bot_id is None:
cls.__used_tokens.add(cls.bot_tokens[0])
return cls.dispatchers[0]
try:
return cls.bots_data[0]
except IndexError:
return None
else:
try:
index = cls.bot_tokens.index(bot_id)
except ValueError:
bot = next(filter(lambda bot_data: bot_data.token == bot_id, cls.bots_data))
except StopIteration:
if not safe:
return None
try:
index = cls.bot_usernames.index(bot_id)
except ValueError:
return None
cls.__used_tokens.add(cls.bot_tokens[index])
return cls.dispatchers[index]
bot = next(filter(lambda bot_data: bot_data.unique_id == bot_id, cls.bots_data))
except StopIteration:
try:
bot = next(filter(lambda bot_data: bot_data.instance.username == bot_id, cls.bots_data))
except StopIteration:
return None
cls.__used_tokens.add(bot.token)
return bot


@classmethod
def get_dispatcher(cls, bot_id=None, safe=True):
bot = cls._get_bot_by_id(bot_id, safe)
if bot:
return bot.dispatcher
else:
return None


@classmethod
Expand All @@ -88,22 +111,11 @@ def getDispatcher(cls, bot_id=None, safe=True):

@classmethod
def get_bot(cls, bot_id=None, safe=True):
if bot_id is None:
if safe:
return cls.bots[0]
else:
return None
bot = cls._get_bot_by_id(bot_id, safe)
if bot:
return bot.instance
else:
try:
index = cls.bot_tokens.index(bot_id)
except ValueError:
if not safe:
return None
try:
index = cls.bot_usernames.index(bot_id)
except ValueError:
return None
return cls.bots[index]
return None


@classmethod
Expand All @@ -113,19 +125,11 @@ def getBot(cls, bot_id=None, safe=True):

@classmethod
def get_updater(cls, bot_id=None, safe=True):
if bot_id is None:
return cls.updaters[0]
bot = cls._get_bot_by_id(bot_id, safe)
if bot:
return bot.updater
else:
try:
index = cls.bot_tokens.index(bot_id)
except ValueError:
if not safe:
return None
try:
index = cls.bot_usernames.index(bot_id)
except ValueError:
return None
return cls.updaters[index]
return None


@classmethod
Expand Down Expand Up @@ -170,43 +174,44 @@ def ready(self):
logger.error('WEBHOOK_CERTIFICATE not found in {} '.format(cert))

for b in bots_list:
token = b.get('TOKEN', None)
context = b.get('CONTEXT', False)
if not token:
break

allowed_updates = b.get('ALLOWED_UPDATES', None)
timeout = b.get('TIMEOUT', None)
proxy = b.get('PROXY', None)
bot = BotData(
token=b['TOKEN'],
unique_id=b.get('ID', None),
use_context=b.get('CONTEXT', False),
allowed_updates=b.get('ALLOWED_UPDATES', None),
timeout=b.get('TIMEOUT', None),
proxy=b.get('PROXY', None),
)

if self.mode == WEBHOOK_MODE:
try:
if b.get('MESSAGEQUEUE_ENABLED',False):
q = mq.MessageQueue(all_burst_limit=b.get('MESSAGEQUEUE_ALL_BURST_LIMIT',29),
all_time_limit_ms=b.get('MESSAGEQUEUE_ALL_TIME_LIMIT_MS',1024))
if proxy:
request = Request(proxy_url=proxy['proxy_url'], urllib3_proxy_kwargs=proxy['urllib3_proxy_kwargs'], con_pool_size=b.get('MESSAGEQUEUE_REQUEST_CON_POOL_SIZE',8))
if bot.proxy:
request = Request(proxy_url=bot.proxy['proxy_url'], urllib3_proxy_kwargs=bot.proxy['urllib3_proxy_kwargs'], con_pool_size=b.get('MESSAGEQUEUE_REQUEST_CON_POOL_SIZE',8))
else:
request = Request(con_pool_size=b.get('MESSAGEQUEUE_REQUEST_CON_POOL_SIZE',8))
bot = MQBot(token, request=request, mqueue=q)
bot.instance = MQBot(bot.token, request=request, mqueue=q)
else:
request = None
if proxy:
request = Request(proxy_url=proxy['proxy_url'], urllib3_proxy_kwargs=proxy['urllib3_proxy_kwargs'])
bot = telegram.Bot(token=token, request=request)

DjangoTelegramBot.dispatchers.append(Dispatcher(bot, None, workers=0, use_context=context))
hookurl = '{}/{}/{}/'.format(webhook_site, webhook_base, token)
max_connections = b.get('WEBHOOK_MAX_CONNECTIONS', 40)
setted = bot.setWebhook(hookurl, certificate=certificate, timeout=timeout, max_connections=max_connections, allowed_updates=allowed_updates)
webhook_info = bot.getWebhookInfo()
real_allowed = webhook_info.allowed_updates if webhook_info.allowed_updates else ["ALL"]

bot.more_info = webhook_info
logger.info('Telegram Bot <{}> setting webhook [ {} ] max connections:{} allowed updates:{} pending updates:{} : {}'.format(bot.username, webhook_info.url, webhook_info.max_connections, real_allowed, webhook_info.pending_update_count, setted))

if bot.proxy:
request = Request(proxy_url=bot.proxy['proxy_url'], urllib3_proxy_kwargs=bot.proxy['urllib3_proxy_kwargs'])
bot.instance = telegram.Bot(token=bot.token, request=request)

bot.dispatcher = Dispatcher(bot.instance, None, workers=0, use_context=bot.use_context)
if not settings.DJANGO_TELEGRAMBOT.get('DISABLE_SETUP', False):
hookurl = '{}/{}/{}/'.format(webhook_site, webhook_base, bot.token)
max_connections = b.get('WEBHOOK_MAX_CONNECTIONS', 40)
setted = bot.instance.setWebhook(hookurl, certificate=certificate, timeout=bot.timeout, max_connections=max_connections, allowed_updates=bot.allowed_updates)
webhook_info = bot.instance.getWebhookInfo()
real_allowed = webhook_info.allowed_updates if webhook_info.allowed_updates else ["ALL"]
bot.more_info = webhook_info
logger.info('Telegram Bot <{}> setting webhook [ {} ] max connections:{} allowed updates:{} pending updates:{} : {}'.format(bot.instance.username, webhook_info.url, webhook_info.max_connections, real_allowed, webhook_info.pending_update_count, setted))
else:
logger.info('Telegram Bot setting webhook without enabling receiving')
except InvalidToken:
logger.error('Invalid Token : {}'.format(token))
logger.error('Invalid Token : {}'.format(bot.token))
return
except RetryAfter as er:
logger.debug('Error: "{}". Will retry in {} seconds'.format(
Expand All @@ -222,14 +227,20 @@ def ready(self):

else:
try:
updater = Updater(token=token, request_kwargs=proxy, use_context=context)
bot = updater.bot
bot.delete_webhook()
DjangoTelegramBot.updaters.append(updater)
DjangoTelegramBot.dispatchers.append(updater.dispatcher)
DjangoTelegramBot.__used_tokens.add(token)
if not settings.DJANGO_TELEGRAMBOT.get('DISABLE_SETUP', False):
bot.updater = Updater(token=bot.token, request_kwargs=bot.proxy, use_context=bot.use_context)
bot.instance = bot.updater.bot
bot.instance.delete_webhook()
bot.dispatcher = bot.updater.dispatcher
DjangoTelegramBot.__used_tokens.add(bot.token)
else:
request = None
if bot.proxy:
request = Request(proxy_url=bot.proxy['proxy_url'], urllib3_proxy_kwargs=bot.proxy['urllib3_proxy_kwargs'])
bot.instance = telegram.Bot(token=bot.token, request=request)
bot.dispatcher = Dispatcher(bot.instance, None, workers=0, use_context=bot.use_context)
except InvalidToken:
logger.error('Invalid Token : {}'.format(token))
logger.error('Invalid Token : {}'.format(bot.token))
return
except RetryAfter as er:
logger.debug('Error: "{}". Will retry in {} seconds'.format(
Expand All @@ -243,12 +254,13 @@ def ready(self):
logger.error('Error: "{}"'.format(er.message))
return

DjangoTelegramBot.bots.append(bot)
DjangoTelegramBot.bot_tokens.append(token)
DjangoTelegramBot.bot_usernames.append(bot.username)
DjangoTelegramBot.bots_data.append(bot)


logger.debug('Telegram Bot <{}> set as default bot'.format(DjangoTelegramBot.bots[0].username))
first_bot = DjangoTelegramBot.bots_data[0]
if not settings.DJANGO_TELEGRAMBOT.get('DISABLE_SETUP', False):
logger.debug('Telegram Bot <{}> set as default bot'.format(first_bot.instance.username))
else:
logger.debug('Telegram Bot <{}> set as default bot'.format(first_bot.unique_id if first_bot.unique_id else first_bot.token))

def module_imported(module_name, method_name, execute):
try:
Expand Down
25 changes: 25 additions & 0 deletions django_telegrambot/bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from telegram import Bot
from telegram.ext import Dispatcher, Updater


class BotData:
def __init__(self,
token,
*,
unique_id=None,
use_context=False,
allowed_updates=None,
timeout=None,
proxy=None,
instance: Bot = None,
dispatcher: Dispatcher = None,
updater: Updater = None):
self.token = token
self.unique_id = unique_id
self.use_context = use_context
self.allowed_updates = allowed_updates
self.timeout = timeout
self.proxy = proxy
self.instance = instance
self.dispatcher = dispatcher
self.updater = updater
2 changes: 1 addition & 1 deletion django_telegrambot/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

@staff_member_required
def home(request):
bot_list = DjangoTelegramBot.bots
bot_list = [bot.instance for bot in DjangoTelegramBot.bots_data]
context = {'bot_list': bot_list, 'update_mode':settings.DJANGO_TELEGRAMBOT.get('MODE', 'WEBHOOK')}
return render(request, 'django_telegrambot/index.html', context)

Expand Down
2 changes: 1 addition & 1 deletion sampleproject/bot/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

# Create your views here.
def index(request):
bot_list = DjangoTelegramBot.bots
bot_list = [bot.instance for bot in DjangoTelegramBot.bots_data]
context = {'bot_list': bot_list, 'update_mode':settings.DJANGO_TELEGRAMBOT['MODE']}
return render(request, 'bot/index.html', context)

0 comments on commit 0e76b6c

Please sign in to comment.