telegram bot
This commit is contained in:
parent
f0ac55c20e
commit
afadc61d5d
@ -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)
|
||||||
|
31
cookbook/migrations/0115_telegrambot.py
Normal file
31
cookbook/migrations/0115_telegrambot.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
@ -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)
|
||||||
|
@ -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'),
|
||||||
|
|
||||||
|
@ -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',
|
||||||
]
|
]
|
||||||
|
@ -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,
|
||||||
|
62
cookbook/views/telegram.py
Normal file
62
cookbook/views/telegram.py
Normal 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({})
|
52
docs/features/telegram_bot.md
Normal file
52
docs/features/telegram_bot.md
Normal 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>`
|
Loading…
Reference in New Issue
Block a user