Wenn ich ein Bild sende, gibt es eine Datumsauswahlaktion zurück und erstellt einen Linienbot, der das ausgewählte Datum auf das Bild zeichnet. Grundsätzlich habe ich es nur möglich gemacht, den Datumsteil dieses Artikels selbst auszuwählen, aber da es verschiedene Stolperpunkte gab, werde ich ihn diesmal zusammenfassen. Referenz: [Python] Ich habe einen LINE-Bot erstellt, der Fotos datiert
--LineBot-Kanalerstellung
main.py
from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (PostbackEvent, TemplateSendMessage, ButtonsTemplate, DatetimePickerTemplateAction,
ImageMessage, ImageSendMessage, MessageEvent, TextMessage, TextSendMessage)
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import datetime
import os
import re
app = Flask(__name__)
app.debug = False
#Umgebungsvariablen abrufen
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]
line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)
#Bildverweispfad
SRC_IMAGE_PATH = "static/images/{}.jpg "
MAIN_IMAGE_PATH = "static/images/{}_main.jpg "
PREVIEW_IMAGE_PATH = "static/images/{}_preview.jpg "
@app.route("/callback", methods=['POST'])
def callback():
signature = request.headers['X-Line-Signature']
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
#Ereignis verfolgen
@handler.add(FollowEvent)
def handle_follow(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=
"Vielen Dank, dass Sie sich als Freund registriert haben. Wenn Sie ein Bild senden und mir das Aufnahmedatum mitteilen, schreibe ich dieses Datum auf das Bild"))
#Rückgabe des Textpapageis
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=event.message.text))
#Bilder empfangen
@handler.add(MessageEvent, message=ImageMessage)
def get_image(event):
global message_id
#message_ID abrufen
message_id = event.message.id
#Dateiname Nachricht_Pfad in ID konvertiert
src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()
#Speichern Sie Bilder vorübergehend in Heroku
save_image(message_id, src_image_path)
#Als Bild speichern, das bei Auswahl von Datum und Uhrzeit angezeigt werden soll
im = Image.open(src_image_path)
im.save(src_image_path)
#Auswahl des Aufnahmedatums
date_picker = TemplateSendMessage(
alt_text='Bitte wählen Sie das Aufnahmedatum',
template=ButtonsTemplate(
text='Bitte wählen Sie das Aufnahmedatum',
thumbnail_image_url=f"https://<Heroku App Name>.herokuapp.com/{src_image_path}",
actions=[
DatetimePickerTemplateAction(
label='Wahl',
data='action=buy&itemid=1',
mode='date',
initial=str(datetime.date.today()),
max=str(datetime.date.today())
)
]
)
)
line_bot_api.reply_message(
event.reply_token,
date_picker
)
#Bilder verarbeiten und senden
@handler.add(PostbackEvent)
def handle_postback(event):
#Dateiname Nachricht_Pfad in ID konvertiert
src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()
main_image_path = MAIN_IMAGE_PATH.format(message_id)
preview_image_path = PREVIEW_IMAGE_PATH.format(message_id)
#Bildverarbeitung
date_the_image(src_image_path, Path(main_image_path).absolute(), event)
date_the_image(src_image_path, Path(preview_image_path).absolute(), event)
#Bild senden
image_message = ImageSendMessage(
original_content_url=f"https://<Heroku App Name>.herokuapp.com/{main_image_path}",
preview_image_url=f"https://<Heroku App Name>.herokuapp.com/{preview_image_path}"
)
#Protokoll abrufen
app.logger.info(f"https://<Heroku App Name>.herokuapp.com/{main_image_path}")
line_bot_api.reply_message(event.reply_token, image_message)
#Bildspeicherfunktion
def save_image(message_id: str, save_path: str) -> None:
# message_Holen Sie sich Binärdaten des Bildes von ID
message_content = line_bot_api.get_message_content(message_id)
with open(save_path, "wb") as f:
#Schreiben Sie die erfassten Binärdaten
for chunk in message_content.iter_content():
f.write(chunk)
#Bildverarbeitungsfunktion
def date_the_image(src: str, desc: str, event) -> None:
im = Image.open(src)
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("./fonts/Helvetica.ttc", 50)
#Rufen Sie das Datum der Datums- und Uhrzeitauswahlaktion ab
text = event.postback.params['date']
#String-Ersetzung durch regulären Ausdruck
text_mod = re.sub("-", "/", text)
#Textgröße
text_width = draw.textsize(text_mod, font=font)[0]
text_height = draw.textsize(text_mod, font=font)[1]
margin = 10
x = im.width - text_width
y = im.height - text_height
#Die Größe des zu zeichnenden Rechtecks
rect_size = ((text_width + margin * 6), (text_height + margin * 2))
#Zeichne ein Rechteck
rect = Image.new("RGB", rect_size, (0, 0, 0))
#Maske, um das Rechteck transparent zu machen
mask = Image.new("L", rect_size, 128)
#Fügen Sie ein Rechteck und eine Maske in das Bild ein
im.paste(rect, (x - margin * 6, y - margin * 3), mask)
#Text schreiben
draw.text((x - margin * 3, y - margin * 2), text_mod, fill=(255, 255, 255), font=font)
im.save(desc)
if __name__ == "__main__":
#app.run()
port = int(os.getenv("PORT", 5000))
app.run(host="0.0.0.0", port=port)
from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (PostbackEvent, TemplateSendMessage, ButtonsTemplate, DatetimePickerTemplateAction,
ImageMessage, ImageSendMessage, MessageEvent, TextMessage, TextSendMessage)
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import datetime
import os
import re
app = Flask(__name__)
app.debug = False
#Umgebungsvariablen abrufen
YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]
line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)
#Bildverweispfad
SRC_IMAGE_PATH = "static/images/{}.jpg "
MAIN_IMAGE_PATH = "static/images/{}_main.jpg "
PREVIEW_IMAGE_PATH = "static/images/{}_preview.jpg "
@app.route("/callback", methods=['POST'])
def callback():
signature = request.headers['X-Line-Signature']
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
Importieren Sie das Modul und stellen Sie die Umgebungsvariablen im Voraus ein. Überprüfen Sie jedoch die entsprechende Rolle.
Legen Sie den Pfad der später beschriebenen Bildreferenzquelle fest und erstellen Sie eine leere Liste zum Speichern von ~~ message_id
. ~~
Ersetzen Sie den Teil {}} durch die Nachricht_ID, wenn das Bild empfangen wurde.
#Ereignis verfolgen
@handler.add(FollowEvent)
def handle_follow(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=
"Vielen Dank, dass Sie sich als Freund registriert haben. Wenn Sie ein Bild senden und mir das Aufnahmedatum mitteilen, schreibe ich dieses Datum auf das Bild"))
Eine Nachricht wird gesendet, wenn ein Benutzer einen Freund hinzufügt.
#Rückgabe des Textpapageis
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=event.message.text))
Ich werde den Text Papagei zurückgeben, aber es ist nicht notwendig.
#Bilder empfangen
@handler.add(MessageEvent, message=ImageMessage)
def get_image(event):
global message_id
#message_ID abrufen
message_id = event.message.id
#Dateiname Nachricht_Pfad in ID konvertiert
src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()
#Speichern Sie Bilder vorübergehend in Heroku
save_image(message_id, src_image_path)
#Als Bild speichern, das bei Auswahl von Datum und Uhrzeit angezeigt werden soll
im = Image.open(src_image_path)
im.save(src_image_path)
#Auswahl des Aufnahmedatums
date_picker = TemplateSendMessage(
alt_text='Bitte wählen Sie das Aufnahmedatum',
template=ButtonsTemplate(
text='Bitte wählen Sie das Aufnahmedatum',
thumbnail_image_url=f"https://<Heroku App Name>.herokuapp.com/{src_image_path}",
actions=[
DatetimePickerTemplateAction(
label='Wahl',
data='action=buy&itemid=1',
mode='date',
initial=str(datetime.date.today()),
max=str(datetime.date.today())
)
]
)
)
line_bot_api.reply_message(
event.reply_token,
date_picker
)
Holen Sie sich die jeder Nachricht zugewiesene ID mit event.message_id
und legen Sie sie global für die Verwendung in anderen Ereignissen fest. ..
~~ Diese ID kann bei anderen Ereignissen nicht abgerufen werden. Speichern Sie sie daher in der zuvor erstellten leeren Nachrichtenliste. ~~
Nachdem Sie die Daten vorübergehend in Heroku gespeichert haben, öffnen Sie die gespeicherten Daten zur Anzeige während der Datumsauswahl und speichern Sie sie erneut als Bild.
Wenn Sie die Datumsauswahlaktion mit "TemplateSendMessage" zurückgeben und den Benutzer auffordern, das Aufnahmedatum auszuwählen, wird angezeigt, ob die URL des Speicherziels mit "thumbnail_image_url" angegeben ist.
#Bilder verarbeiten und senden
@handler.add(PostbackEvent)
def handle_postback(event):
#Dateiname Nachricht_Pfad in ID konvertiert
src_image_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()
main_image_path = MAIN_IMAGE_PATH.format(message_id)
preview_image_path = PREVIEW_IMAGE_PATH.format(message_id)
#Bildverarbeitung
date_the_image(src_image_path, Path(main_image_path).absolute(), event)
date_the_image(src_image_path, Path(preview_image_path).absolute(), event)
#Bild senden
image_message = ImageSendMessage(
original_content_url=f"https://<Heroku App Name>.herokuapp.com/{main_image_path}",
preview_image_url=f"https://<Heroku App Name>.herokuapp.com/{preview_image_path}"
)
#Protokoll abrufen
app.logger.info(f"https://<Heroku App Name>.herokuapp.com/{main_image_path}")
line_bot_api.reply_message(event.reply_token, image_message)
Ruft das im oben genannten MessageEvent (ImageMessage) empfangene Bild ab. ~~ Holen Sie sich die ID, die der Name des Bildes sein wird, aus message_list
, um zu bestimmen.
Wenn Bilder kontinuierlich gesendet werden, wird die neue ID gespeichert, bevor die Daten initialisiert werden. Geben Sie daher die letzte (letzte) mit "message_list [-1]" an.
Holen Sie sich das vom Benutzer ausgewählte Datum mit "handl_postback (event)" und weisen Sie es "text" der Funktion "data_the_image" zu, um Text usw. zu schreiben.
Wenn der Vorgang abgeschlossen ist, speichern Sie das Bild und senden Sie es zum Abschluss an den Benutzer zurück.
#Bildspeicherfunktion
def save_image(message_id: str, save_path: str) -> None:
# message_Holen Sie sich Binärdaten des Bildes von ID
message_content = line_bot_api.get_message_content(message_id)
with open(save_path, "wb") as f:
#Schreiben Sie die erfassten Binärdaten
for chunk in message_content.iter_content():
f.write(chunk)
Eigentlich wollte ich die Exif-Informationen des Bildes abrufen und das Aufnahmedatum automatisch eingeben, aber ich konnte es mit dieser Methode nicht abrufen, daher nahm ich die Form der obigen Datumsauswahlaktion als sorgfältige Maßnahme. Wenn jemand weiß, wie es geht, lass es mich wissen.
#Bildverarbeitungsfunktion
def date_the_image(src: str, desc: str, event) -> None:
im = Image.open(src)
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("./fonts/Helvetica.ttc", 50)
#Rufen Sie das Datum der Datums- und Uhrzeitauswahlaktion ab
text = event.postback.params['date']
#String-Ersetzung durch regulären Ausdruck
text_mod = re.sub("-", "/", text)
#Textgröße
text_width = draw.textsize(text_mod, font=font)[0]
text_height = draw.textsize(text_mod, font=font)[1]
margin = 10
x = im.width - text_width
y = im.height - text_height
#Die Größe des zu zeichnenden Rechtecks
rect_size = ((text_width + margin * 6), (text_height + margin * 2))
#Zeichne ein Rechteck
rect = Image.new("RGB", rect_size, (0, 0, 0))
#Maske, um das Rechteck transparent zu machen
mask = Image.new("L", rect_size, 128)
#Fügen Sie ein Rechteck und eine Maske in das Bild ein
im.paste(rect, (x - margin * 6, y - margin * 3), mask)
#Text schreiben
draw.text((x - margin * 3, y - margin * 2), text_mod, fill=(255, 255, 255), font=font)
im.save(desc)
Ich wollte, dass es bis zu einem gewissen Grad gut aussieht, also habe ich etwas Verarbeitung hinzugefügt.
Holen Sie sich das vom Benutzer ausgewählte Datum mit "event.postback.params [" Datum "] und ändern Sie" JJJJ-MM-TT "in" JJJJ / "mit" re.sub ("-", "/", Text) " In das Format "MM / DD" konvertieren.
Verwenden Sie ((text_width + margin * 6), (text_height + margin * 2))
, um die Größe des Rechtecks und der Maske festzulegen, und lassen Sie einen Rand von 30 Pixel links und rechts und 10 Pixel oben und unten im Text.
Geben Sie abschließend die Position des Rechtecks und der Maske mit im.paste (rect, (x - Rand * 6, y - Rand * 3), Maske) an und fügen Sie sie ein und zeichnen Sie text ( Schreiben Sie den Text mit (x - Rand * 3, y - Rand * 2), text_mod, fill = (255, 255, 255), font = font) `und fertig.
if __name__ == "__main__":
#app.run()
port = int(os.getenv("PORT", 5000))
app.run(host="0.0.0.0", port=port)
Dieser Linienbot war die erste App, die ich nach dem Lernen selbst gemacht habe, aber es dauerte ungefähr ein halbes Jahr (durchschnittlich 1 bis 2 Stunden pro Tag), um sie zu erstellen. Ungefähr 80% der Produktionszeit wurde damit verbracht, zu untersuchen, was ich nicht verstand, und es gab Zeiten, in denen es ziemlich schwierig war. Tatsächlich gab es Zeiten, in denen ich meinen Computer ungefähr eine Woche lang nicht geöffnet habe, aber ich habe nie daran gedacht, das Programmieren aufzugeben, also glaube ich, dass ich Spaß daran hatte.
Eigentlich gab es Drehungen und Wendungen von dem ersten, was ich machen wollte, und ich entschied mich für diese Form, aber ich lernte, dass es sehr wichtig ist, den gesamten Fluss bis zu einem gewissen Grad im Voraus zu machen. Es ist jedoch schwierig zu verstehen, welche Art von Dingen mit wie viel Aufwand und wenig Erfahrung erledigt werden können. Letztendlich denke ich, dass es keine andere Wahl gibt, als stetig mit Versuch und Irrtum fortzufahren.
Derzeit studiere ich die Datenbank und versuche, den Namen und das Geburtsdatum der Person auf dem Bild zu speichern, damit ich vom Aufnahmedatum zurückrechnen und anzeigen kann, wie alt das Bild ist, sobald es fertig ist Ich werde es veröffentlichen.
Recommended Posts