telegram bot

This commit is contained in:
vabene1111 2021-03-18 22:34:53 +01:00
parent f0ac55c20e
commit afadc61d5d
8 changed files with 175 additions and 3 deletions

View File

@ -7,7 +7,7 @@ from .models import (Comment, CookLog, Food, Ingredient, InviteLink, Keyword,
RecipeBook, RecipeBookEntry, RecipeImport, ShareLink, RecipeBook, RecipeBookEntry, RecipeImport, ShareLink,
ShoppingList, ShoppingListEntry, ShoppingListRecipe, ShoppingList, ShoppingListEntry, ShoppingListRecipe,
Space, Step, Storage, Sync, SyncLog, Unit, UserPreference, Space, Step, Storage, Sync, SyncLog, Unit, UserPreference,
ViewLog, Supermarket, SupermarketCategory, SupermarketCategoryRelation, ImportLog) ViewLog, Supermarket, SupermarketCategory, SupermarketCategoryRelation, ImportLog, TelegramBot)
class CustomUserAdmin(UserAdmin): class CustomUserAdmin(UserAdmin):
@ -220,3 +220,10 @@ class ImportLogAdmin(admin.ModelAdmin):
admin.site.register(ImportLog, ImportLogAdmin) admin.site.register(ImportLog, ImportLogAdmin)
class TelegramBotAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'created_by',)
admin.site.register(TelegramBot, TelegramBotAdmin)

View File

@ -0,0 +1,31 @@
# Generated by Django 3.1.7 on 2021-03-18 21:12
import cookbook.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cookbook', '0114_importlog'),
]
operations = [
migrations.CreateModel(
name='TelegramBot',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.CharField(max_length=256)),
('name', models.CharField(blank=True, default='', max_length=128)),
('chat_id', models.CharField(blank=True, default='', max_length=128)),
('webhook_token', models.UUIDField(default=uuid.uuid4)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('space', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cookbook.space')),
],
bases=(models.Model, cookbook.models.PermissionModelMixin),
),
]

View File

@ -617,6 +617,20 @@ class InviteLink(models.Model, PermissionModelMixin):
return f'{self.uuid}' return f'{self.uuid}'
class TelegramBot(models.Model, PermissionModelMixin):
token = models.CharField(max_length=256)
name = models.CharField(max_length=128, default='', blank=True)
chat_id = models.CharField(max_length=128, default='', blank=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
webhook_token = models.UUIDField(default=uuid.uuid4)
objects = ScopedManager(space='space')
space = models.ForeignKey(Space, on_delete=models.CASCADE)
def __str__(self):
return f"{self.name}"
class CookLog(models.Model, PermissionModelMixin): class CookLog(models.Model, PermissionModelMixin):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE) recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
created_by = models.ForeignKey(User, on_delete=models.CASCADE) created_by = models.ForeignKey(User, on_delete=models.CASCADE)

View File

@ -11,7 +11,7 @@ from cookbook.helper import dal
from .models import (Comment, Food, InviteLink, Keyword, MealPlan, Recipe, from .models import (Comment, Food, InviteLink, Keyword, MealPlan, Recipe,
RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList, RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList,
Storage, Sync, SyncLog, get_model_name) Storage, Sync, SyncLog, get_model_name)
from .views import api, data, delete, edit, import_export, lists, new, views from .views import api, data, delete, edit, import_export, lists, new, views, telegram
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r'user-name', api.UserNameViewSet, basename='username') router.register(r'user-name', api.UserNameViewSet, basename='username')
@ -100,6 +100,10 @@ urlpatterns = [
path('dal/food/', dal.IngredientsAutocomplete.as_view(), name='dal_food'), path('dal/food/', dal.IngredientsAutocomplete.as_view(), name='dal_food'),
path('dal/unit/', dal.UnitAutocomplete.as_view(), name='dal_unit'), path('dal/unit/', dal.UnitAutocomplete.as_view(), name='dal_unit'),
path('telegram/setup/<int:pk>', telegram.setup_bot, name='telegram_setup'),
path('telegram/remove/<int:pk>', telegram.remove_bot, name='telegram_remove'),
path('telegram/hook/<slug:token>/', telegram.hook, name='telegram_hook'),
path('docs/markdown/', views.markdown_info, name='docs_markdown'), path('docs/markdown/', views.markdown_info, name='docs_markdown'),
path('docs/api/', views.api_info, name='docs_api'), path('docs/api/', views.api_info, name='docs_api'),

View File

@ -6,6 +6,7 @@ import cookbook.views.import_export
import cookbook.views.lists import cookbook.views.lists
import cookbook.views.new import cookbook.views.new
import cookbook.views.views import cookbook.views.views
import cookbook.views.telegram
__all__ = [ __all__ = [
'api', 'api',
@ -16,4 +17,5 @@ __all__ = [
'lists', 'lists',
'new', 'new',
'views', 'views',
'telegram',
] ]

View File

@ -28,7 +28,7 @@ from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest,
CustomIsOwner, CustomIsShare, CustomIsOwner, CustomIsShare,
CustomIsShared, CustomIsUser, CustomIsShared, CustomIsUser,
group_required) group_required)
from cookbook.helper.recipe_url_import import get_from_html, get_from_scraper from cookbook.helper.recipe_url_import import get_from_html, get_from_scraper, find_recipe_json
from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan, from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
MealType, Recipe, RecipeBook, ShoppingList, MealType, Recipe, RecipeBook, ShoppingList,
ShoppingListEntry, ShoppingListRecipe, Step, ShoppingListEntry, ShoppingListRecipe, Step,

View File

@ -0,0 +1,62 @@
import json
import requests
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import render, get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from cookbook.helper.ingredient_parser import parse
from cookbook.helper.permission_helper import group_required
from cookbook.models import TelegramBot, ShoppingList, ShoppingListEntry, Food, Unit
@group_required('user')
def setup_bot(request, pk):
bot = get_object_or_404(TelegramBot, pk=pk, space=request.space)
hook_url = f'{request.build_absolute_uri("/")}telegram/hook/{bot.webhook_token}/'
create_response = requests.get(f'https://api.telegram.org/bot{bot.token}/setWebhook?url={hook_url}')
info_response = requests.get(f'https://api.telegram.org/bot{bot.token}/getWebhookInfo')
return JsonResponse({'hook_url': hook_url, 'create_response': json.loads(create_response.content.decode()), 'info_response': json.loads(info_response.content.decode())}, json_dumps_params={'indent': 4})
@group_required('user')
def remove_bot(request, pk):
bot = get_object_or_404(TelegramBot, pk=pk, space=request.space)
remove_response = requests.get(f'https://api.telegram.org/bot{bot.token}/deleteWebhook')
info_response = requests.get(f'https://api.telegram.org/bot{bot.token}/getWebhookInfo')
return JsonResponse({'remove_response': json.loads(remove_response.content.decode()), 'info_response': json.loads(info_response.content.decode())}, json_dumps_params={'indent': 4})
@csrf_exempt
def hook(request, token):
print(request.POST, request.body, token)
tb = get_object_or_404(TelegramBot, webhook_token=token)
data = json.loads(request.body.decode())
if tb.chat_id == '':
tb.chat_id = data['message']['chat']['id']
tb.save()
if tb.chat_id == str(data['message']['chat']['id']):
sl = ShoppingList.objects.filter(Q(created_by=tb.created_by)).filter(finished=False, space=tb.space).order_by('-created_at').first()
if sl:
print(f'found shopping list {sl} adding {data["message"]["text"]}')
amount, unit, ingredient, note = parse(data['message']['text'])
f, created = Food.objects.get_or_create(name=ingredient, space=tb.space)
u, created = Unit.objects.get_or_create(name=unit, space=tb.space)
sl.entries.add(
ShoppingListEntry.objects.create(
food=f, unit=u, amount=amount
)
)
return JsonResponse({'data': data['message']['text']})
return JsonResponse({})

View File

@ -0,0 +1,52 @@
The telegram bot is meant to simplify certain interactions with Tandoor.
It is currently very basic but might be expanded in the future.
!!! warning "Experimental"
This feature is considered experimental. You can use it and it should not break anything but you might be
required to update your configuration in the future.
The setup is also definitely not user-friendly, this will likely improve if the feature is well-received/expanded.
!!! info "Public IP/Domain"
To use the Telegram Bot you will need an installation that is accessible from the outside, otherwise telegram can't send messages.
This could be circumvented using the polling API but this is currently not implemented.
## Shopping Bot
The shopping bot will add any message you send it to your latest open shopping list.
To get a shopping bot follow these steps
1. Create a new Telegram Bot using the [BotFather](https://t.me/botfather)
- If you want to use the bot with multiple persons add the bot to a group and grant it admin privileges
2. Open the Admin Page (click your username, then admin) and select `Telegram Bots`
3. Create a new Bot
- token: the token obtained in step one
- space: your space (usually Default)
- user: to the user the bot is meant for (determines the shopping list used)
- chat id: if you know where messages will be sent from enter the chat ID, otherwise it is set to the first chat the bot received a message from
4. Visit your installation at `recipes.mydomin.tld/telegram/setup/<botid>` with botid being the ID of the bot you just created
You should see the following message:
```
{
"hook_url": "https://recipes.mydomin.tld/telegram/hook/c0c08de9-5e1e-4480-8312-3e256af61340/",
"create_response": {
"ok": true,
"result": true,
"description": "Webhook was set"
},
"info_response": {
"ok": true,
"result": {
"url": "recipes.mydomin.tld/telegram/hook/<webhook_token>",
"has_custom_certificate": false,
"pending_update_count": 0,
"max_connections": 40,
"ip_address": "46.4.105.116"
}
}
}
```
You should now be able to send messages to the bot and have the entries appear in your latest shopping list.
### Resetting
To reset a bot open `recipes.mydomin.tld/telegram/remove/<botid>`