aboutsummaryrefslogtreecommitdiff
path: root/bot
diff options
context:
space:
mode:
authorclarkzjw <[email protected]>2023-02-28 23:48:18 -0800
committerclarkzjw <[email protected]>2023-02-28 23:48:18 -0800
commitf853b99bc68b7df7fa22f9b21ec42bdc8eef9dea (patch)
treea30dbc7f1b56b5a12eced3c1e931d695c48de0aa /bot
parenta975ef06d47d17debcb766a6af4c03ff5f8859ed (diff)
downloadphoto-f853b99bc68b7df7fa22f9b21ec42bdc8eef9dea.tar.gz
temporarily move python files to subdirectory to fix cloudflare pages build
Diffstat (limited to 'bot')
-rw-r--r--bot/bot.py174
-rw-r--r--bot/requirements.txt21
2 files changed, 195 insertions, 0 deletions
diff --git a/bot/bot.py b/bot/bot.py
new file mode 100644
index 0000000..9d9b322
--- /dev/null
+++ b/bot/bot.py
@@ -0,0 +1,174 @@
1import io
2import os
3import logging
4import traceback
5from PIL import Image
6import boto3
7from telegram import Update
8from telegram.constants import ParseMode
9from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters
10from peewee import *
11from uuid import uuid4
12from datetime import datetime
13import json
14
15db = SqliteDatabase("database/photos.db")
16db.connect(reuse_if_open=True)
17
18
19class BaseModel(Model):
20 class Meta:
21 database = db
22
23
24class Photo(BaseModel):
25 guid = CharField(unique=True, primary_key=True)
26 fileId = CharField(max_length=256)
27 width = IntegerField()
28 height = IntegerField()
29 ratio = FloatField()
30 orientation = CharField(max_length=128)
31 path = CharField(max_length=256)
32 caption = CharField(max_length=256)
33 alt = CharField(max_length=256)
34 createdAt = DateTimeField()
35 uploadedAt = DateTimeField()
36
37
38with db.connection_context():
39 db.create_tables([Photo])
40
41logging.basicConfig(
42 format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO
43)
44
45logger = logging.getLogger(__name__)
46
47
48async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
49 await update.message.reply_text("This is a bot to output image in square shape")
50
51
52bucket_name = "pixel-jinwei-me"
53cf_account_id = os.getenv("CF_ACCOUNT_ID")
54aws_access_key_id = os.getenv("CF_R2_KEY_ID")
55aws_secret_access_key = os.getenv("CF_R2_ACCESS_KEY_SECRET")
56
57
58def write_json() -> bool:
59 with db.connection_context():
60 photos = Photo.select()
61 results = []
62 for photo in photos:
63 results.append({
64 "guid": photo.guid,
65 "fileId": photo.fileId,
66 "width": photo.width,
67 "height": photo.height,
68 "ratio": photo.ratio,
69 "orientation": photo.orientation,
70 "path": photo.path,
71 "caption": photo.caption,
72 "alt": photo.alt,
73 "createdAt": photo.createdAt.strftime("%Y-%m-%dT%H:%M:%S.000Z"),
74 "uploadedAt": photo.uploadedAt.strftime("%Y-%m-%dT%H:%M:%S.000Z")
75 })
76 with open("database/photos.json", "w") as f:
77 f.write(json.dumps(results))
78 return True
79
80
81def upload_to_s3(key_name: str, file: bytes):
82 endpoint_ca_central = "https://{}.r2.cloudflarestorage.com".format(cf_account_id)
83
84 client = boto3.client("s3",
85 region_name="auto",
86 endpoint_url=endpoint_ca_central,
87 aws_access_key_id=aws_access_key_id,
88 aws_secret_access_key=aws_secret_access_key)
89
90 response = client.list_buckets()
91 buckets = [b["Name"] for b in response['Buckets']]
92
93 if bucket_name not in buckets:
94 print("{} doesn't exist".format(bucket_name))
95 print('Existing buckets:')
96 for bucket in response['Buckets']:
97 print(f' {bucket["Name"]}')
98 return False
99
100 response = client.put_object(Body=file, Bucket=bucket_name, Key=key_name, ContentType="image/webp")
101 status = response["ResponseMetadata"]["HTTPStatusCode"]
102 if status == 200:
103 print("upload {} to {} succeed".format(key_name, bucket_name))
104 return True
105 else:
106 print("upload {} to {} failed, status: {}".format(key_name, bucket_name, status))
107 return False
108
109
110async def process(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
111 chat_id = update.message.chat_id
112
113 if update.message.document is not None:
114 names = update.message.document.file_name.split(".")
115 file_ext = names[1]
116 filename = names[0]
117
118 if str.upper(file_ext) not in ("JPG", "JPEG", "PNG"):
119 await context.bot.send_message(chat_id, "Image extension `{}` not supported".format(file_ext),
120 parse_mode=ParseMode.MARKDOWN_V2)
121 return
122
123 file = await update.message.effective_attachment.get_file()
124 else:
125 return
126
127 await context.bot.send_message(chat_id, "Processing `{}`".format(filename), parse_mode=ParseMode.MARKDOWN_V2)
128
129 img = io.BytesIO()
130 await file.download_to_memory(img)
131
132 try:
133 im = Image.open(img)
134 output = io.BytesIO()
135 im.save(output, format="webp", lossless=False, quality=80)
136
137 now = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
138 key_name = "{}-{}.webp".format(now, filename)
139
140 with db.connection_context():
141 photo = Photo.create(guid=str.upper(str(uuid4())),
142 fileId=key_name,
143 width=im.width,
144 height=im.height,
145 ratio=im.width / im.height,
146 orientation="landscape" if im.width > im.height else "portrait",
147 path="https://pixelstatic.jinwei.me/{}".format(key_name),
148 caption="",
149 alt="",
150 createdAt=datetime.now(),
151 uploadedAt=datetime.now())
152
153 output.seek(0)
154 upload_to_s3(photo.fileId, output.read())
155
156 write_json()
157 await update.message.reply_markdown_v2(text="Done")
158
159 except Exception:
160 await update.message.reply_markdown_v2(text="Error:\n```{}```".format(traceback.format_exc()))
161
162
163def main() -> None:
164 tg_token = os.getenv("TG_TOKEN")
165 application = Application.builder().token(tg_token).build()
166
167 application.add_handler(CommandHandler("start", start))
168 application.add_handler(MessageHandler(filters.ATTACHMENT & ~filters.COMMAND, process))
169
170 application.run_polling()
171
172
173if __name__ == "__main__":
174 main()
diff --git a/bot/requirements.txt b/bot/requirements.txt
new file mode 100644
index 0000000..788075c
--- /dev/null
+++ b/bot/requirements.txt
@@ -0,0 +1,21 @@
1anyio==3.6.2
2boto3==1.26.81
3botocore==1.29.81
4certifi==2022.12.7
5h11==0.14.0
6h2==4.1.0
7hpack==4.0.0
8httpcore==0.16.3
9httpx==0.23.3
10hyperframe==6.0.1
11idna==3.4
12jmespath==1.0.1
13peewee==3.16.0
14Pillow==9.4.0
15python-dateutil==2.8.2
16python-telegram-bot==20.1
17rfc3986==1.5.0
18s3transfer==0.6.0
19six==1.16.0
20sniffio==1.3.0
21urllib3==1.26.14
Powered by cgit v1.2.3 (git 2.41.0)