From c840ceed500bf34a1c3e6290bfe9e64319ed918f Mon Sep 17 00:00:00 2001 From: JungDev Date: Thu, 25 May 2017 15:44:57 +0000 Subject: [PATCH] Version 1.0.0 --- HISTORY.rst | 11 ++- README.rst | 73 +++++++++++++++++-- django_telegrambot/__init__.py | 2 +- django_telegrambot/apps.py | 67 ++++++++--------- .../management/commands/botpolling.py | 32 ++++++-- django_telegrambot/models.py | 2 +- django_telegrambot/urls.py | 13 ++-- django_telegrambot/views.py | 2 +- docs/usage.rst | 51 ++++++++++++- sampleproject/bot/telegrambot.py | 21 ++++++ sampleproject/bot/templates/bot/index.html | 16 +--- sampleproject/bot/views.py | 3 +- sampleproject/manage.py | 2 +- .../sampleproject/local_settings.sample.py | 65 +++++++++++++++-- sampleproject/sampleproject/urls.py | 2 +- 15 files changed, 276 insertions(+), 86 deletions(-) mode change 100644 => 100755 sampleproject/manage.py diff --git a/HISTORY.rst b/HISTORY.rst index 1b13410..c53645d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,9 +2,18 @@ History ------- +1.0.0 (2017-05-25) +++++++++++++++++++ +* IMPORTANT: If you upgrade from a previous version, you MUST change how to include django_telegrambot.urls and settings.py. +* Added admin dashboard, available at /admin/django-telegrambot +* Added polling mode from management command (an easy to way to run bot in local machine, not recommended in production) +* More setting available +* Improved AppConfig +* Improved sample project + 0.2.6 (2017-04-08) ++++++++++++++++++ -* Improve module loading +* Improved module loading * Added sample project 0.2.5 (2017-03-06) diff --git a/README.rst b/README.rst index 0148e50..85522b0 100644 --- a/README.rst +++ b/README.rst @@ -38,18 +38,75 @@ And set your bots:: #settings.py #Django Telegram Bot settings - TELEGRAM_BOT_TOKENS = ('BOT_1_token','BOT_2_token',) - TELEGRAM_WEBHOOK_SITE = 'https://mysite.it' - TELEGRAM_WEBHOOK_BASE = '/baseurl' - #TELEGRAM_WEBHOOK_CERTIFICATE = 'cert.pem' #If your site use self-signed certificate, must be set with location of your public key certificate. (More info at https://core.telegram.org/bots/self-signed ) + DJANGO_TELEGRAMBOT = { -Include in your urls.py the ``django_telegrambot.urls`` using the same value of ``TELEGRAM_WEBHOOK_BASE`` :: + 'MODE' : 'WEBHOOK', #(Optional [str]) # The default value is WEBHOOK, + # otherwise you may use 'POLLING' + # NB: if use polling you must provide to run + # a management command that starts a worker + + 'WEBHOOK_SITE' : 'https://mywebsite.com', + 'WEBHOOK_PREFIX' : '/prefix', # (Optional[str]) # If this value is specified, + # a prefix is added to webhook url + + #'WEBHOOK_CERTIFICATE' : 'cert.pem', # If your site use self-signed + #certificate, must be set with location of your public key + #certificate.(More info at https://core.telegram.org/bots/self-signed ) + + 'BOTS' : [ + { + 'TOKEN': '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', #Your bot token. + + #'ALLOWED_UPDATES':(Optional[list[str]]), # List the types of + #updates you want your bot to receive. For example, specify + #``["message", "edited_channel_post", "callback_query"]`` to + #only receive updates of these types. See ``telegram.Update`` + #for a complete list of available update types. + #Specify an empty list to receive all updates regardless of type + #(default). If not specified, the previous setting will be used. + #Please note that this parameter doesn't affect updates created + #before the call to the setWebhook, so unwanted updates may be + #received for a short period of time. + + #'TIMEOUT':(Optional[int|float]), # If this value is specified, + #use it as the read timeout from the server + + #'WEBHOOK_MAX_CONNECTIONS':(Optional[int]), # Maximum allowed number of + #simultaneous HTTPS connections to the webhook for update + #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. + + #'POLL_INTERVAL' : (Optional[float]), # Time to wait between polling updates from Telegram in + #seconds. Default is 0.0 + + #'POLL_CLEAN':(Optional[bool]), # Whether to clean any pending updates on Telegram servers before + #actually starting to poll. Default is False. + + #'POLL_BOOTSTRAP_RETRIES':(Optional[int]), # Whether the bootstrapping phase of the `Updater` + #will retry on failures on the Telegram server. + #| < 0 - retry indefinitely + #| 0 - no retries (default) + #| > 0 - retry up to X times + + #'POLL_READ_LATENCY':(Optional[float|int]), # Grace time in seconds for receiving the reply from + #server. Will be added to the `timeout` value and used as the read timeout from + #server (Default: 2). + }, + #Other bots here with same structure. + ], + + } + + + +Include in your urls.py the ``django_telegrambot.urls`` (NB: If you upgrade from a previous version, you MUST change how to include ``django_telegrambot.urls``. Never set prefix here!):: #urls.py urlpatterns = [ ... - url(r'^baseurl/', include('django_telegrambot.urls')), + url(r'^', include('django_telegrambot.urls')), ... ] @@ -85,7 +142,7 @@ Then use it in a project creating a module ``telegrambot.py`` in your app :: def main(): logger.info("Loading handlers for telegram bot") - # Default dispatcher (this is related to the first bot in settings.TELEGRAM_BOT_TOKENS) + # 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_token') #get by bot token @@ -148,7 +205,7 @@ There a sample application in `sampleproject` directory. Here is installation in 5. To test webhook locally install `ngrok` application and run command ./ngrok http 8000 -And change TELEGRAM_WEBHOOK_SITE and ALLOWED_HOSTS in local_settings.py file +6. Change `WEBHOOK_SITE` and `ALLOWED_HOSTS` in local_settings.py file Credits --------- diff --git a/django_telegrambot/__init__.py b/django_telegrambot/__init__.py index e4a581e..ac36e53 100644 --- a/django_telegrambot/__init__.py +++ b/django_telegrambot/__init__.py @@ -1,2 +1,2 @@ -__version__ = '0.2.7' +__version__ = '1.0.0' default_app_config = 'django_telegrambot.apps.DjangoTelegramBot' diff --git a/django_telegrambot/apps.py b/django_telegrambot/apps.py index aeb6d44..38b77e1 100644 --- a/django_telegrambot/apps.py +++ b/django_telegrambot/apps.py @@ -29,8 +29,6 @@ class DjangoTelegramBot(AppConfig): name = 'django_telegrambot' verbose_name = 'Django TelegramBot' ready_run = False - #_dispatcher = None - #_updater = None bot_tokens = [] bot_usernames = [] dispatchers = [] @@ -127,57 +125,55 @@ def ready(self): DjangoTelegramBot.ready_run = True self.mode = WEBHOOK_MODE - if hasattr(settings, 'TELEGRAM_BOT_MODE'): - if settings.TELEGRAM_BOT_MODE == 'POLLING': - self.mode = POLLING_MODE + if settings.DJANGO_TELEGRAMBOT.get('MODE', 'WEBHOOK') == 'POLLING': + self.mode = POLLING_MODE + modes = ['WEBHOOK','POLLING'] logger.info('Django Telegram Bot <{} mode>'.format(modes[self.mode])) - if not hasattr(settings, 'TELEGRAM_BOT_TOKENS'): - logger.warn('Required TELEGRAM_BOT_TOKENS missing in settings') - return - tokens = settings.TELEGRAM_BOT_TOKENS + bots_list = settings.DJANGO_TELEGRAMBOT.get('BOTS', []) if self.mode == WEBHOOK_MODE: - if not hasattr(settings, 'TELEGRAM_WEBHOOK_SITE'): + webhook_site = settings.DJANGO_TELEGRAMBOT.get('WEBHOOK_SITE', None) + if not webhook_site: logger.warn('Required TELEGRAM_WEBHOOK_SITE missing in settings') return - webhook_site = settings.TELEGRAM_WEBHOOK_SITE + if webhook_site.endswith("/"): + webhook_site = webhook_site[:-1] - webhook_base = "" - if hasattr(settings, 'TELEGRAM_WEBHOOK_BASE'): - logger.warn('Required TELEGRAM_WEBHOOK_BASE missing in settings') - webhook_base = settings.TELEGRAM_WEBHOOK_BASE + webhook_base = settings.DJANGO_TELEGRAMBOT.get('WEBHOOK_PREFIX','/') + if webhook_base.startswith("/"): + webhook_base = webhook_base[1:] + if webhook_base.endswith("/"): + webhook_base = webhook_base[:-1] + cert = settings.DJANGO_TELEGRAMBOT.get('WEBHOOK_CERTIFICATE', None) certificate = None - if hasattr(settings, 'TELEGRAM_WEBHOOK_CERTIFICATE'): - cert = settings.TELEGRAM_WEBHOOK_CERTIFICATE - if os.path.exists(cert): - logger.info('TELEGRAM_WEBHOOK_CERTIFICATE found in {}'.format(cert)) - certificate=open(cert, 'rb') - else: - logger.error('TELEGRAM_WEBHOOK_CERTIFICATE not found in {} '.format(cert)) + if cert and os.path.exists(cert): + logger.info('WEBHOOK_CERTIFICATE found in {}'.format(cert)) + certificate=open(cert, 'rb') + elif cert: + logger.error('WEBHOOK_CERTIFICATE not found in {} '.format(cert)) - timeout = None - if hasattr(settings, 'TELEGRAM_WEBHOOK_TIMEOUT'): - timeout = settings.TELEGRAM_WEBHOOK_TIMEOUT + for b in bots_list: + token = b.get('TOKEN', None) + if not token: + break - max_connections = 40 - if hasattr(settings, 'TELEGRAM_WEBHOOK_MAX_CONNECTIONS'): - max_connections = settings.TELEGRAM_WEBHOOK_MAX_CONNECTIONS + allowed_updates = b.get('ALLOWED_UPDATES', None) + timeout = b.get('TIMEOUT', None) - allowed_updates = None - if hasattr(settings, 'TELEGRAM_WEBHOOK_ALLOWED_UPDATES'): - allowed_updates = settings.TELEGRAM_WEBHOOK_ALLOWED_UPDATES - - for token in tokens: if self.mode == WEBHOOK_MODE: bot = telegram.Bot(token=token) DjangoTelegramBot.dispatchers.append(Dispatcher(bot, None, workers=0)) - hookurl = '{}{}/{}/'.format(webhook_site, webhook_base, token) + 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)) else: @@ -224,6 +220,3 @@ def module_exists(module_name, method_name, execute): updater = DjangoTelegramBot.get_updater(bot_id=token) logger.info('python manage.py botpolling --username={}'.format(updater.bot.username)) - - - diff --git a/django_telegrambot/management/commands/botpolling.py b/django_telegrambot/management/commands/botpolling.py index ba6744d..235542b 100644 --- a/django_telegrambot/management/commands/botpolling.py +++ b/django_telegrambot/management/commands/botpolling.py @@ -29,8 +29,8 @@ def get_updater(self, username=None, token=None): def handle(self, *args, **options): from django.conf import settings - if not settings.TELEGRAM_BOT_MODE == 'POLLING': - self.stderr.write("Webhook mode active, change it in your settings if you want use polling update") + if settings.DJANGO_TELEGRAMBOT.get('MODE', 'WEBHOOK') == 'WEBHOOK': + self.stderr.write("Webhook mode active in settings.py, change in POLLING if you want use polling update") return updater = self.get_updater(username=options.get('username'), token=options.get('token')) @@ -48,10 +48,30 @@ def handle(self, *args, **options): console.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s')) logger.addHandler(console) - #wbinfo = updater.bot.getWebhookInfo() - #logger.warn(wbinfo) - #updater.bot.deleteWebhook() #not yet present in python-telegram-bot 5.3.1 + + bots_list = settings.DJANGO_TELEGRAMBOT.get('BOTS', []) + b = None + for bot_set in bots_list: + if bot_set.get('TOKEN', None) == updater.bot.token: + b = bot_set + break + if not b: + self.stderr.write("Cannot find bot settings") + return + + allowed_updates = b.get('ALLOWED_UPDATES', None) + timeout = b.get('TIMEOUT', 10) + poll_interval = b.get('POLL_INTERVAL', 0.0) + clean = b.get('POLL_CLEAN', False) + bootstrap_retries = b.get('POLL_BOOTSTRAP_RETRIES', 0) + read_latency = b.get('POLL_READ_LATENCY', 2.) + self.stdout.write("Run polling...") - updater.start_polling() + updater.start_polling(poll_interval=poll_interval, + timeout=timeout, + clean=clean, + bootstrap_retries=bootstrap_retries, + read_latency=read_latency, + allowed_updates=allowed_updates) self.stdout.write("the bot is started and runs until we press Ctrl-C on the command line.") updater.idle() \ No newline at end of file diff --git a/django_telegrambot/models.py b/django_telegrambot/models.py index 71a8362..d49766e 100644 --- a/django_telegrambot/models.py +++ b/django_telegrambot/models.py @@ -1,3 +1,3 @@ from django.db import models -# Create your models here. +# Create your models here. \ No newline at end of file diff --git a/django_telegrambot/urls.py b/django_telegrambot/urls.py index 64549b5..f348adc 100644 --- a/django_telegrambot/urls.py +++ b/django_telegrambot/urls.py @@ -3,14 +3,11 @@ from . import views from django.conf import settings -if hasattr(settings, 'TELEGRAM_WEBHOOK_BASE'): - webhook_base = settings.TELEGRAM_WEBHOOK_BASE - if webhook_base.startswith("/"): - webhook_base = webhook_base[1:] - if not webhook_base.endswith("/"): - webhook_base += "/" -else: - webhook_base = "" +webhook_base = settings.DJANGO_TELEGRAMBOT.get('WEBHOOK_PREFIX','/') +if webhook_base.startswith("/"): + webhook_base = webhook_base[1:] +if not webhook_base.endswith("/"): + webhook_base += "/" urlpatterns = [ url(r'admin/django-telegrambot/$', views.home, name='django-telegrambot'), diff --git a/django_telegrambot/views.py b/django_telegrambot/views.py index 8412939..37d76e7 100644 --- a/django_telegrambot/views.py +++ b/django_telegrambot/views.py @@ -20,7 +20,7 @@ @staff_member_required def home(request): bot_list = DjangoTelegramBot.bots - context = {'bot_list': bot_list, 'update_mode':settings.TELEGRAM_BOT_MODE} + context = {'bot_list': bot_list, 'update_mode':settings.DJANGO_TELEGRAMBOT.get('MODE', 'WEBHOOK')} return render(request, 'django_telegrambot/index.html', context) diff --git a/docs/usage.rst b/docs/usage.rst index 95afbb6..9d80bf4 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -2,6 +2,53 @@ Usage ======== -To use django-telegrambot in a project:: +To use django-telegrambot in a app, create a telegrambot.py module in your app as follow:: + + # Example code for telegrambot.py module + from telegram.ext import CommandHandler, MessageHandler, Filters + from django_telegrambot.apps import DjangoTelegramBot + + import logging + logger = logging.getLogger(__name__) + + + # Define a few command handlers. These usually take the two arguments bot and + # update. Error handlers also receive the raised TelegramError object in error. + def start(bot, update): + bot.sendMessage(update.message.chat_id, text='Hi!') + + + def help(bot, update): + bot.sendMessage(update.message.chat_id, text='Help!') + + + def echo(bot, update): + bot.sendMessage(update.message.chat_id, text=update.message.text) + + + def error(bot, update, error): + logger.warn('Update "%s" caused error "%s"' % (update, error)) + + + def main(): + logger.info("Loading handlers for telegram bot") + + # 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_token') #get by bot token + # dp = DjangoTelegramBot.getDispatcher('BOT_n_username') #get by bot username + + # on different commands - answer in Telegram + dp.add_handler(CommandHandler("start", start)) + dp.add_handler(CommandHandler("help", help)) + + # on noncommand i.e message - echo the message on Telegram + dp.add_handler(MessageHandler([Filters.text], echo)) + + # log all errors + dp.add_error_handler(error) + + # log all errors + dp.addErrorHandler(error) - import django_telegrambot diff --git a/sampleproject/bot/telegrambot.py b/sampleproject/bot/telegrambot.py index 43bf222..4906200 100644 --- a/sampleproject/bot/telegrambot.py +++ b/sampleproject/bot/telegrambot.py @@ -13,6 +13,22 @@ def start(bot, update): bot.sendMessage(update.message.chat_id, text='Hi!') +def startgroup(bot, update): + bot.sendMessage(update.message.chat_id, text='Hi!') + + +def me(bot, update): + bot.sendMessage(update.message.chat_id, text='Your information:\n{}'.format(update.effective_user)) + + +def chat(bot, update): + bot.sendMessage(update.message.chat_id, text='This chat information:\n {}'.format(update.effective_chat)) + + +def forwarded(bot, update): + bot.sendMessage(update.message.chat_id, text='This msg forwaded information:\n {}'.format(update.effective_message)) + + def help(bot, update): bot.sendMessage(update.message.chat_id, text='Help!') @@ -38,6 +54,11 @@ def main(): dp.add_handler(CommandHandler("start", start)) dp.add_handler(CommandHandler("help", help)) + dp.add_handler(CommandHandler("startgroup", startgroup)) + dp.add_handler(CommandHandler("me", me)) + dp.add_handler(CommandHandler("chat", chat)) + dp.add_handler(MessageHandler(Filters.forwarded , forwarded)) + # on noncommand i.e message - echo the message on Telegram dp.add_handler(MessageHandler(Filters.text, echo)) diff --git a/sampleproject/bot/templates/bot/index.html b/sampleproject/bot/templates/bot/index.html index fca8289..2790eae 100644 --- a/sampleproject/bot/templates/bot/index.html +++ b/sampleproject/bot/templates/bot/index.html @@ -1,23 +1,13 @@ {% extends "base_generic.html" %} {% block content %} -

Django-TelegramBot

+ +

Django-TelegramBot Sample App

Welcome in the sample project of Django-Telegrambot, aka how add Telegram bots to your Django app!
The full documentation is at https://django-telegrambot.readthedocs.org.

+

Visit Django-Telegrambot Dashboard to see more info about your telegram bots

-{% if bot_list %} -

Bot's List:

- -{% else %} -

No bots are available. Please configure it in settings.py

-{% endif %} {% endblock %} diff --git a/sampleproject/bot/views.py b/sampleproject/bot/views.py index 6dffab3..f1ee467 100644 --- a/sampleproject/bot/views.py +++ b/sampleproject/bot/views.py @@ -1,8 +1,9 @@ from django.shortcuts import render +from django.conf import settings from django_telegrambot.apps import DjangoTelegramBot # Create your views here. def index(request): bot_list = DjangoTelegramBot.bots - context = {'bot_list': bot_list} + context = {'bot_list': bot_list, 'update_mode':settings.TELEGRAM_BOT_MODE} return render(request, 'bot/index.html', context) diff --git a/sampleproject/manage.py b/sampleproject/manage.py old mode 100644 new mode 100755 index c395c15..b20ac34 --- a/sampleproject/manage.py +++ b/sampleproject/manage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python import os import sys -import sys + path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(path) diff --git a/sampleproject/sampleproject/local_settings.sample.py b/sampleproject/sampleproject/local_settings.sample.py index b325b94..6ddf1c5 100644 --- a/sampleproject/sampleproject/local_settings.sample.py +++ b/sampleproject/sampleproject/local_settings.sample.py @@ -1,7 +1,62 @@ #settings.py -#Django Telegram Bot settings -TELEGRAM_BOT_TOKENS = ('BOT_1_token', 'BOT_2_token',) -TELEGRAM_WEBHOOK_SITE = 'https://mysite.it' -TELEGRAM_WEBHOOK_BASE = '/baseurl' -#TELEGRAM_WEBHOOK_CERTIFICATE = 'cert.pem' #If your site use self-signed certificate, must be set with location of your public key certificate. (More info at https://core.telegram.org/bots/self-signed ) +DJANGO_TELEGRAMBOT = { + + 'MODE' : 'WEBHOOK', #(Optional [str]) # The default value is WEBHOOK, + # otherwise you may use 'POLLING' + # NB: if use polling mode you must provide to run + # a management command that starts a worker + + 'WEBHOOK_SITE' : 'https://mywebsite.com', + 'WEBHOOK_PREFIX' : '/prefix', # (Optional[str]) # If this value is specified, + # a prefix is added to webhook url + + #'WEBHOOK_CERTIFICATE' : 'cert.pem', # If your site use self-signed + #certificate, must be set with location of your public key + #certificate.(More info at https://core.telegram.org/bots/self-signed ) + + 'BOTS' : [ + { + 'TOKEN': '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', #Your bot token. + + #'ALLOWED_UPDATES':(Optional[list[str]]), # List the types of + #updates you want your bot to receive. For example, specify + #``["message", "edited_channel_post", "callback_query"]`` to + #only receive updates of these types. See ``telegram.Update`` + #for a complete list of available update types. + #Specify an empty list to receive all updates regardless of type + #(default). If not specified, the previous setting will be used. + #Please note that this parameter doesn't affect updates created + #before the call to the setWebhook, so unwanted updates may be + #received for a short period of time. + + #'TIMEOUT':(Optional[int|float]), # If this value is specified, + #use it as the read timeout from the server + + #'WEBHOOK_MAX_CONNECTIONS':(Optional[int]), # Maximum allowed number of + #simultaneous HTTPS connections to the webhook for update + #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. + + #'POLL_INTERVAL' : (Optional[float]), # Time to wait between polling updates from Telegram in + #seconds. Default is 0.0 + + #'POLL_CLEAN':(Optional[bool]), # Whether to clean any pending updates on Telegram servers before + #actually starting to poll. Default is False. + + #'POLL_BOOTSTRAP_RETRIES':(Optional[int]), # Whether the bootstrapping phase of the `Updater` + #will retry on failures on the Telegram server. + #| < 0 - retry indefinitely + #| 0 - no retries (default) + #| > 0 - retry up to X times + + #'POLL_READ_LATENCY':(Optional[float|int]), # Grace time in seconds for receiving the reply from + #server. Will be added to the `timeout` value and used as the read timeout from + #server (Default: 2). + }, + #Other bots here with same structure. + ], + +} + ALLOWED_HOSTS = ['', ] diff --git a/sampleproject/sampleproject/urls.py b/sampleproject/sampleproject/urls.py index a1b33ed..4befeeb 100644 --- a/sampleproject/sampleproject/urls.py +++ b/sampleproject/sampleproject/urls.py @@ -18,6 +18,6 @@ urlpatterns = [ url(r'^admin/', admin.site.urls), - url(r'^baseurl/', include('django_telegrambot.urls')), + url(r'^', include('django_telegrambot.urls')), url(r'^$', include('bot.urls')), ]