From 4d92f700ade1a3ad5d945da8acee370ae1693263 Mon Sep 17 00:00:00 2001 From: woni <320753691@qq.com> Date: Fri, 19 Jan 2024 00:37:31 +0800 Subject: [PATCH] add celery --- .env | 4 + Dockerfile | 2 +- app/__init__.py | 107 ++++++++ app/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 3154 bytes app/__pycache__/make_celery.cpython-310.pyc | Bin 0 -> 245 bytes app/__pycache__/task.cpython-310.pyc | Bin 0 -> 845 bytes app/__pycache__/utils.cpython-310.pyc | Bin 0 -> 4220 bytes app/demo.py | 117 --------- app/make_celery.py | 4 + app/task.py | 81 ++++++ app/tmp.py | 1 + docker-compose.yaml | 31 ++- poetry.lock | 276 +++++++++++++++++++- pyproject.toml | 2 + 14 files changed, 493 insertions(+), 132 deletions(-) create mode 100644 app/__init__.py create mode 100644 app/__pycache__/__init__.cpython-310.pyc create mode 100644 app/__pycache__/make_celery.cpython-310.pyc create mode 100644 app/__pycache__/task.cpython-310.pyc create mode 100644 app/__pycache__/utils.cpython-310.pyc delete mode 100644 app/demo.py create mode 100644 app/make_celery.py create mode 100644 app/task.py create mode 100644 app/tmp.py diff --git a/.env b/.env index cd64bc6..c2c33a6 100644 --- a/.env +++ b/.env @@ -13,3 +13,7 @@ VERIFICATION_TOKEN=35LaoAPYTXRyBmjeaBvzRbRUsJSL1KBx # BITABLE BITABLE_TOKEN=XDmHbhbZcaY4gZsOvBJcLcHonKh TABLE_ID=tblwgGCDCE74Ugv6 + +# REDIS +BROKER_URL=redis://office-xbot-redis +RESULT_BACKEND=redis://office-xbot-redis diff --git a/Dockerfile b/Dockerfile index 948906d..0a1d991 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,4 +30,4 @@ COPY ./app /code/app # -CMD ["python", "/code/app/demo.py"] +# CMD ["python", "/code/app/demo.py"] diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..8ddb8ad --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,107 @@ +import os +import json +import time + +import lark_oapi as lark +from celery import Celery +from celery import Task + + +from flask import Flask +from dotenv import load_dotenv +from itertools import chain +from lark_oapi.adapter.flask import * +from lark_oapi.api.im.v1 import * +from lark_oapi.api.bitable.v1 import * + +from app.task import * +from app.utils import * +from app.task import * + +load_dotenv() + + +app_id = os.environ["APP_ID"] +app_secret = os.environ["APP_SECRET"] + +encrypt_key = os.environ["ENCRYPT_KEY"] +verification_token = os.environ["VERIFICATION_TOKEN"] + +bitable_token = os.environ["BITABLE_TOKEN"] +table_id = os.environ["TABLE_ID"] + +bitable_token = "IRggb4I0MaRffTsJ7qkcw1wxnBg" +table_id = "tbl5J3sXBNIyNvJ8" + +broker_url = os.environ["BROKER_URL"] +result_backend = os.environ["RESULT_BACKEND"] + + +# 创建client +client = lark.Client.builder().app_id(app_id).app_secret(app_secret).build() + +current_dir = os.path.dirname(os.path.abspath(__file__)) +cache_dir = os.path.join(current_dir, "cache") + +if not os.path.exists(cache_dir): + os.makedirs(cache_dir) + + +def do_p2_im_message_receive_v1(data: P2ImMessageReceiveV1) -> None: + process_message_callback.delay(client, bitable_token, table_id, cache_dir, data) + print(lark.JSON.marshal(data)) + + +def do_customized_event(data: lark.CustomizedEvent) -> None: + print(lark.JSON.marshal(data)) + + +handler = ( + lark.EventDispatcherHandler.builder( + encrypt_key, verification_token, lark.LogLevel.DEBUG + ) + .register_p2_im_message_receive_v1(do_p2_im_message_receive_v1) + .register_p1_customized_event("message", do_customized_event) + .build() +) + + +def create_app() -> Flask: + app = Flask(__name__) + + app.config.from_mapping( + CELERY=dict( + broker_url=broker_url, + result_backend=result_backend, + task_ignore_result=True, + broker_connection_retry_on_startup=True, + ), + ) + app.config.from_prefixed_env() + celery_init_app(app) + + @app.route("/event", methods=["POST"]) + def event(): + print("receive event: ", time.time()) + resp = handler.do(parse_req()) + return parse_resp(resp) + + @app.route("/test", methods=["GET"]) + def test(): + add.delay(1, 2) + return {"test": "ok"} + + return app + + +def celery_init_app(app: Flask) -> Celery: + class FlaskTask(Task): + def __call__(self, *args: object, **kwargs: object) -> object: + with app.app_context(): + return self.run(*args, **kwargs) + + celery_app = Celery(app.name, task_cls=FlaskTask) + celery_app.config_from_object(app.config["CELERY"]) + celery_app.set_default() + app.extensions["celery"] = celery_app + return celery_app diff --git a/app/__pycache__/__init__.cpython-310.pyc b/app/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d08cec9ce7ed10bc4fc1ce24bc1f45575770a3a GIT binary patch literal 3154 zcmai0-ESMm5x>1V9={~nmTAe~v0cXvos^24egLiO#*!FEu}T{fox%j##fiI=PCDK( zdrR38WT1cyG%s%7`qT#!=!^cPMc?}5=e`96oZl=dS&kd@4mUeH`!zHBo0&@|Crb{V zKR-B>|0+7pzo~Qb$)WQ(zU&{&afBmS?6gKLYcbn*Tdr-rmXFqrbKRg7Fq-3m&$sfH z_v1ph*eY5+7ni!_R@w4FJkgzOO;>K0Y;WPmkL(<952~$nt^19O-PG6@?2< zt75q#xO0{(ft#^h8QiSpCcvGy+$6XQmYV{1(Q>E2U6TIR<-wJqGpNYQkPXhsbD&p; zPU{2Axi+|N=?(dlAse#RP22lW-V~>YZtIrq{Z!r>vh45D8GJ+>>^UQ=Vp_0{FV2d} zOTTp+5`XLCvFx0ff$S4Vem1xwXXGrn+Sc6A8+?> z&1d4GEJ6}s=S$);BtI8y&8c2_P50KQG7~JySgKucelyIr(dNF0M@%_RL%~I=WwJx^ z_GTC*VAyRF+<*L-FE5!AwQKdo)q2yE>y5?Lr;nTbVg0F@{<^-p{KfL({pRwc25&xk zSZ|ogh2`e`g_ZiaTNrg{mxWbKSmjSvSIpFEeeKCglP}y~#F{0X8n!p3@kOXZ6DXbr%{wy6Hv4m7uQ$helth~I zT5s1}5-IQ9;Zc`&WtN2-k}KJk(T?Oh^K+z{3sPa~3Ys6)U!BFMA|uo}A{U8}LL+h+ zoH>hw+WkzY-Dpn=E_Y<2KcgvEiF`m)@?#a+=o~*DxI8}cQ??00emY<;oPB4@-SV`r zb2<6EH8@*D|h$d z`|4z)S@lh6U8UPn@xF@9l#*FL)_gr|Z_7lOX${2jXd_7#`HjZRjq&_;nk2HVqcp*Y zR=XTbrbDIsJzz0dtgqBppY8>7))B^k{AjHSIb5JOQ<1&nf+MJ`kqd5E7YL@=fDN4k z7e)d!+CKnnUN|~O219wbv3F{Glh^DKzrI!t)C?9IUq@YO@|$5I5JJWksVVjVCo&TjaKvcPHwtgb&VGaVRAyo;qD0)E$#?% z>JG>gd>O^rgbN+d|1TYB$7)H@Bvar#3A>VW6W{_n(T17ms1(5gtD%%}%In1Elm;xBD2*u7MO- z;JWPS_5T~ZDHRHA$}O>}T#5O|>{ft^QjqTsFvH&6Fj_fYT zx6Z&BI)6KD@(O%?^Aq~X>IlbrpmY@j;q)5 zIRLBr3GU6$WZY4|!wKqh5ae9)_=(&G)Bx@==Nxhy6SyFD^UdDJ|KG~m(b`5er+$g` zro?$S75$h{IKlZh{V+blWV*hEoTBQ-$1fdo7DL5j=lzK^Aq*6pt4u9}K{OX^zG}yr z`Vay@d{jg8ih!a<@kK|O=@Iz3Mfdp7U(u3sCN&qb6H=-x!ACO5km9ncH}VM`WJ+VJ z3GOx;t(G<#vLDnRjRqz)jNDH^H%0+F2=e=AE9^$`T7NHveAVJ}j%=&OfB zNKvb@$#tmak3u&XReVK%bEX(+sdSpcRKlm3J>I7lim->lnq6wno*3vwwWwR$nK$R( z6|P4*T#qG<6kt=0yl9HV^>q|yCXcX>RGOH8z!M2mqBfHVXl=?eX{+6y=G$`DOz%h) zp(cm6E@%{%#7vI2=c6v}2xnoJ6Imu$j3bmwlV9&gu|UqwSq2w4lKZ_-Z<@S_l)b%t zxSm-FaM_70j>_%6QrLoH4*Z)&i7DDY6e|ei^C;7qDRjeai5Z!xJifEs{r%{VV0H9E z@b$d?2{5M?kE?i{iukB@&scsbqJ#0{qrMzhm%>WAv4ScPo7_@;;mHGYUC9j`hKl-Q zza8fC+bQ$!{WGX4-M-Cx1ep_A)I!aMLE-KwfbLP<$QmF8q*A=7RCqn2whswXtXJ|% jtc0X`^j-8T?i8DVWZJ9v0aC5|Ch!V?sr%hjY3B0xS${eK literal 0 HcmV?d00001 diff --git a/app/__pycache__/make_celery.cpython-310.pyc b/app/__pycache__/make_celery.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bcde861e517a42fb75edabc25faa1f180f4482c GIT binary patch literal 245 zcmYjJyAFat5ZpVEC{cSmTN1G*wK)aTO#r}!a;=Eukn|zwelCNT(EJHoz3jd zj9x5!AiNJxl(b)i_y;F(q%mtE1&ZZRu#ho8_JEKhzd4GpAas>;hQNK33j${SxJKDv zB&*7LG3HqJr(j|o()CbjXGtziHvWC=8Z1q!OGCNQJKUd8PsusbXDhC{KylY)DtaY! rlB6+4-9z4A9&kYR literal 0 HcmV?d00001 diff --git a/app/__pycache__/task.cpython-310.pyc b/app/__pycache__/task.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d779ea837a22e01f1701d3a21dcfdff1e76e7ce6 GIT binary patch literal 845 zcmY*X&2AGh5VpPE&8FRy{vfqN@Bk|zaz_XuQQ#I((^DiAZFVN@wwr9Qy+EXJD%ZXN zhjQ#I`3jf30w-o1Bw9-|{>Gm1d^6*w*V_`bo*dueZz;qtHtwzt8Lvp}XHps z(6we>DvtiQ7_+=4u_>vQSjj^{N#x*G8`Pht{qtsq{V&bD>Mx<+Os7?e z3OJ$~@v=Wq1UGyc-_$Nr^R@rQwknF55>H@#4{hj`^gSTwv)! k@SRlQ4}^T_i&+D0jW4-2yH6?=naX4u9Z9Vzv3@N70?LD|KmY&$ literal 0 HcmV?d00001 diff --git a/app/__pycache__/utils.cpython-310.pyc b/app/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4005dcdc54110a6fef87e22b7c26ac433f755580 GIT binary patch literal 4220 zcmbVPTW{RP73O7l*_*Ult!}pCs%?5%$5}~E;yOkU)QaQW8o7|I7!iUH)XuC#io0Zo zT-jPsPw8vWB2NW^0@Pdl(3k#<{)7Sg)TjJ_7N~=uJ!fVmt&}xtl#Bg_!#SKeocZQE zXX09|tikok%2T$mplScW%J^kK<=5~}{{%oasz=(kD)nt0>PBSr%xzOw&&aj~&&wzXM z=K&YOMX*|;7PY?we^hJB*%-D5uXf)}gAea?wzmEI_xSn;tHG!K&5fID(d)y5_1(RV zC;Z0hYP9yztsCzh+#TNPL;>q(?VTX=cOuq4SZn)9;>n7~StsVSJxHjZvE{BGM2ucv z?!=VcUVi3WU+$&5a8tg%9L2j{KyNR9OJKU5>cS5C6nbr45|_8+%XgCGA@;v1d)#Ed z8?ZDJ&dwl+DC43e$Bg$`U$|=MfQphD9y%6PuHNu=VtOd1&i5wlZond%3QKk;NaXW< zQ5y|%Kgoi)FJ=ZQwxiTcH_7Pk6q;iLokmayR~cfnbUaUOF) za2|vSb0EsQEc4VH2om(Wu^=BlxW6Hsp3l=gKN18c&~LfIRbIiFwuJ*TJ}h zOM7zJaC5pvzJki6Es|Ms3)ph2IC66U9ue3GZipMCg4^cE%B`Xb+u&E3+#FQTt*{RC zH=vx6+rcUt7q$Uf&Vqe2oCB+QN~oo_0oHJHfB@JVFGTJRXFnu2#}DKV0;&PWZ*47! z%kN0z^Yk0>j91aHh9H*{XPM6+G!SMHKutBih_D0zY>?gY%lM>;a0THdgsTWYLRd!l zF~Uy}eu^MzVypcjiPN(A&#>X=2y%Qh;lDs=As}rOyaIQk>{B|T!z$F%WdKD7Nrvyr zCF2|kE}3P6qvbiJ#A!LD!C#>=i3Un3Vc{)Ucsq1Lw@W)Aiz4>Dk;Le2CK$j2M{Ic5jrE>)IVSz z4fq*>M$NKky?c>PO%%Y3KK}-KO_7kcp>zh+<-5A$LQN@4gH-i~UIvY+e@{$)7fjEo zOa&g`_LG75Z*Y@op{~O4XT}kMFpG5)!}5`xkzA)b5aE3wg;m%Pu$|16rKDR?mVvt> z=~#sTtQWQ10i2S`1bF0T(_vk{F+(xPy0O1r?Aq|F%zl9Iywh~Np?nKeY zj0XNV6FG{LSej_CjuBRIq3`#Ys3bmzQ0_@$oK*T4`iLCsZS+E_n^dqltj=Ti7UjVK zQ+&31=z=H@q)VP3MM~->$y!jo+!p*bepJq z)sY9%9|l(G4S>2))vJbW7&06xvdUoiZ&EV;>DI>s~*1+R5XfRhU|$Wtx+ zBUH|7VMZ_*h88H7{-s{nl3)(SEb=UGG=Xdo7{f|YMVdmWBt;Dr^37~2oG$8+5fT{^ zEhrnHkT$f>o++fXY68xM^I)++9qNu{hb2sgole|`v^N9UVNbG$!`3A2Yjc)FhtCzg zF%~Y0*-f-Pl|7l*saPnbMKWz+f(`PFe**I)Tu7l5y{6^3UeNclJrpU$tD9h?Xyl9} zMp)AD1@v%I;A~lA(uW?LsoX_fYox4CySg(Z&~Ba%qn`rjnEJtB!YA_OHLCKf{JL!vB|Cop7XcI>2*NsjB42h?#!@d&Lgmz?q!Jhi8XBF@L^AYB*#?@(3UkKpCc`{7G_i{D!e@BlAp|V!ChjVmgS} h+d5dJn7g`~t5 None: - print(lark.JSON.marshal(data)) - - # record = {} - # images = [] - # images_token = [] - # message_content = "" - # message_type = data.event.message.message_type - # content = json.loads(data.event.message.content) - - # if message_type == "post": - # content = content["content"] - # for item in chain.from_iterable(content): - # if item.get("tag") == "text": - # message_content += item["text"] - - # if item.get("tag") == "img": - # images.append(item["image_key"]) - - # if message_type == "text": - # message_content = content["text"].replace("@_user_1", "").strip() - - # sender_id = data.event.sender.sender_id.open_id - # record["问题概述"] = message_content - # record["创建人"] = sender_id - # message_id = data.event.message.message_id - - # # 对 image_key 处理并并上传至多维表格 - - # for image in images: - # image_path = get_message_resource(client, message_id, image, "image", cache_dir) - # print("下载文件成功:", image, image_path) - # file_token = upload_media(client, image_path, "bitable_image", bitable_token) - # print("上传文件成功:", file_token) - # images_token.append(file_token) - - # if images: - # record["附件"] = [{"file_token": image} for image in images_token] - - # record_id = add_record(client, bitable_token, table_id, record) - - # time.sleep(1) - - # update_data = {"创建人": {"id": sender_id}} - # update_record(client, bitable_token, table_id, record_id, update_data) - - # # content = {"text": "反馈的问题已经记录到多维表格, 稍后有小伙伴跟进"} - # # send_message(receive_id, content) - - # # 回复群组的消息 - # content = f'{{"text": "反馈的问题已经记录到多维表格, 稍后有小伙伴跟进"}}' - # send_group_message(client, message_id, content) - - -def do_customized_event(data: lark.CustomizedEvent) -> None: - print(lark.JSON.marshal(data)) - - -handler = ( - lark.EventDispatcherHandler.builder( - encrypt_key, verification_token, lark.LogLevel.DEBUG - ) - .register_p2_im_message_receive_v1(do_p2_im_message_receive_v1) - .register_p1_customized_event("message", do_customized_event) - .build() -) - - -@app.route("/event", methods=["POST"]) -def event(): - print("receive event: ", time.time()) - resp = handler.do(parse_req()) - return parse_resp(resp) - - -if __name__ == "__main__": - app.run(port=8080, debug=True, host="0.0.0.0") diff --git a/app/make_celery.py b/app/make_celery.py new file mode 100644 index 0000000..1878ad2 --- /dev/null +++ b/app/make_celery.py @@ -0,0 +1,4 @@ +from app import create_app + +flask_app = create_app() +celery_app = flask_app.extensions["celery"] diff --git a/app/task.py b/app/task.py new file mode 100644 index 0000000..1b6249c --- /dev/null +++ b/app/task.py @@ -0,0 +1,81 @@ +import time +import json + +from itertools import chain + +from celery import shared_task +from celery import Task + +from app.utils import * + + +@shared_task(ignore_result=False) +def add(a: int, b: int) -> int: + return a + b + + +@shared_task() +def block() -> None: + time.sleep(5) + + +@shared_task(bind=True, ignore_result=False) +def process(self: Task, total: int) -> object: + for i in range(total): + self.update_state(state="PROGRESS", meta={"current": i + 1, "total": total}) + time.sleep(1) + + return {"current": total, "total": total} + + +@shared_task(include_result=False) +def process_message_callback(client, bitable_token, table_id, cache_dir, data) -> None: + record = {} + images = [] + images_token = [] + message_content = "" + message_type = data.event.message.message_type + content = json.loads(data.event.message.content) + + if message_type == "post": + content = content["content"] + for item in chain.from_iterable(content): + if item.get("tag") == "text": + message_content += item["text"] + + if item.get("tag") == "img": + images.append(item["image_key"]) + + if message_type == "text": + message_content = content["text"].replace("@_user_1", "").strip() + + sender_id = data.event.sender.sender_id.open_id + record["问题概述"] = message_content + record["创建人"] = sender_id + message_id = data.event.message.message_id + + # 对 image_key 处理并并上传至多维表格 + + for image in images: + image_path = get_message_resource(client, message_id, image, "image", cache_dir) + print("下载文件成功:", image, image_path) + file_token = upload_media(client, image_path, "bitable_image", bitable_token) + print("上传文件成功:", file_token) + images_token.append(file_token) + + if images: + record["附件"] = [{"file_token": image} for image in images_token] + + record_id = add_record(client, bitable_token, table_id, record) + + time.sleep(1) + + update_data = {"创建人": {"id": sender_id}} + update_record(client, bitable_token, table_id, record_id, update_data) + + # content = {"text": "反馈的问题已经记录到多维表格, 稍后有小伙伴跟进"} + # send_message(receive_id, content) + + # 回复群组的消息 + content = f'{{"text": "反馈的问题已经记录到多维表格, 稍后有小伙伴跟进"}}' + send_group_message(client, message_id, content) diff --git a/app/tmp.py b/app/tmp.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/app/tmp.py @@ -0,0 +1 @@ + diff --git a/docker-compose.yaml b/docker-compose.yaml index a5f1c2e..63d5fc9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,8 +1,9 @@ version: '3' services: - office-xbot-feishu: - container_name: office-xbot-feishu-0 + office-xbot-app: + container_name: office-xbot-app image: "${APP_IMAGE}" + command: "flask --app app:create_app run --debug --port 8080 --host 0.0.0.0" ports: - "0.0.0.0:8080:8080" environment: @@ -12,3 +13,29 @@ services: - VERIFICATION_TOKEN=${VERIFICATION_TOKEN} - BITABLE_TOKEN=${BITABLE_TOKEN} - TABLE_ID=${TABLE_ID} + - BROKER_URL=${BROKER_URL} + - RESULT_BACKEND=${RESULT_BACKEND} + + office-xbot-celery: + container_name: office-xbot-celery + image: "${APP_IMAGE}" + command: "celery -A app.make_celery worker --loglevel INFO" + environment: + - APP_ID=${APP_ID} + - APP_SECRET=${APP_SECRET} + - ENCRYPT_KEY=${ENCRYPT_KEY} + - VERIFICATION_TOKEN=${VERIFICATION_TOKEN} + - BITABLE_TOKEN=${BITABLE_TOKEN} + - TABLE_ID=${TABLE_ID} + - BROKER_URL=${BROKER_URL} + - RESULT_BACKEND=${RESULT_BACKEND} + + office-xbot-redis: + container_name: office-xbot-redis + image: redis:latest + ports: + - "6379:6379" + +networks: + office-xbot-network: + external: true diff --git a/poetry.lock b/poetry.lock index e162c8b..26d174e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,40 @@ -# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "amqp" +version = "5.2.0" +description = "Low-level AMQP client for Python (fork of amqplib)." +optional = false +python-versions = ">=3.6" +files = [ + {file = "amqp-5.2.0-py3-none-any.whl", hash = "sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637"}, + {file = "amqp-5.2.0.tar.gz", hash = "sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd"}, +] + +[package.dependencies] +vine = ">=5.0.0,<6.0.0" + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "billiard" +version = "4.2.0" +description = "Python multiprocessing fork with improvements and bugfixes" +optional = false +python-versions = ">=3.7" +files = [ + {file = "billiard-4.2.0-py3-none-any.whl", hash = "sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d"}, + {file = "billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c"}, +] [[package]] name = "blinker" @@ -11,6 +47,61 @@ files = [ {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, ] +[[package]] +name = "celery" +version = "5.3.6" +description = "Distributed Task Queue." +optional = false +python-versions = ">=3.8" +files = [ + {file = "celery-5.3.6-py3-none-any.whl", hash = "sha256:9da4ea0118d232ce97dff5ed4974587fb1c0ff5c10042eb15278487cdd27d1af"}, + {file = "celery-5.3.6.tar.gz", hash = "sha256:870cc71d737c0200c397290d730344cc991d13a057534353d124c9380267aab9"}, +] + +[package.dependencies] +billiard = ">=4.2.0,<5.0" +click = ">=8.1.2,<9.0" +click-didyoumean = ">=0.3.0" +click-plugins = ">=1.1.1" +click-repl = ">=0.2.0" +kombu = ">=5.3.4,<6.0" +python-dateutil = ">=2.8.2" +tzdata = ">=2022.7" +vine = ">=5.1.0,<6.0" + +[package.extras] +arangodb = ["pyArango (>=2.0.2)"] +auth = ["cryptography (==41.0.5)"] +azureblockblob = ["azure-storage-blob (>=12.15.0)"] +brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +cassandra = ["cassandra-driver (>=3.25.0,<4)"] +consul = ["python-consul2 (==0.1.5)"] +cosmosdbsql = ["pydocumentdb (==2.3.5)"] +couchbase = ["couchbase (>=3.0.0)"] +couchdb = ["pycouchdb (==1.14.2)"] +django = ["Django (>=2.2.28)"] +dynamodb = ["boto3 (>=1.26.143)"] +elasticsearch = ["elastic-transport (<=8.10.0)", "elasticsearch (<=8.11.0)"] +eventlet = ["eventlet (>=0.32.0)"] +gevent = ["gevent (>=1.5.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +memcache = ["pylibmc (==1.6.3)"] +mongodb = ["pymongo[srv] (>=4.0.2)"] +msgpack = ["msgpack (==1.0.7)"] +pymemcache = ["python-memcached (==1.59)"] +pyro = ["pyro4 (==4.82)"] +pytest = ["pytest-celery (==0.0.0)"] +redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] +s3 = ["boto3 (>=1.26.143)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +solar = ["ephem (==4.1.5)"] +sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] +sqs = ["boto3 (>=1.26.143)", "kombu[sqs] (>=5.3.0)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] +zstd = ["zstandard (==0.22.0)"] + [[package]] name = "certifi" version = "2023.11.17" @@ -135,6 +226,55 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "click-didyoumean" +version = "0.3.0" +description = "Enables git-like *did-you-mean* feature in click" +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, +] + +[package.dependencies] +click = ">=7" + +[[package]] +name = "click-plugins" +version = "1.1.1" +description = "An extension module for click to enable registering CLI commands via setuptools entry-points." +optional = false +python-versions = "*" +files = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] + +[package.dependencies] +click = ">=4.0" + +[package.extras] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] + +[[package]] +name = "click-repl" +version = "0.3.0" +description = "REPL plugin for Click" +optional = false +python-versions = ">=3.6" +files = [ + {file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"}, + {file = "click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812"}, +] + +[package.dependencies] +click = ">=7.0" +prompt-toolkit = ">=3.0.36" + +[package.extras] +testing = ["pytest (>=7.2.1)", "pytest-cov (>=4.0.0)", "tox (>=4.4.3)"] + [[package]] name = "colorama" version = "0.4.6" @@ -207,6 +347,38 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "kombu" +version = "5.3.5" +description = "Messaging library for Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "kombu-5.3.5-py3-none-any.whl", hash = "sha256:0eac1bbb464afe6fb0924b21bf79460416d25d8abc52546d4f16cad94f789488"}, + {file = "kombu-5.3.5.tar.gz", hash = "sha256:30e470f1a6b49c70dc6f6d13c3e4cc4e178aa6c469ceb6bcd55645385fc84b93"}, +] + +[package.dependencies] +amqp = ">=5.1.1,<6.0.0" +vine = "*" + +[package.extras] +azureservicebus = ["azure-servicebus (>=7.10.0)"] +azurestoragequeues = ["azure-identity (>=1.12.0)", "azure-storage-queue (>=12.6.0)"] +confluentkafka = ["confluent-kafka (>=2.2.0)"] +consul = ["python-consul2"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=4.1.1)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=4.5.2,!=4.5.5,<6.0.0)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy (>=1.4.48,<2.1)"] +sqs = ["boto3 (>=1.26.143)", "pycurl (>=7.43.0.5)", "urllib3 (>=1.26.16)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=2.8.0)"] + [[package]] name = "lark-oapi" version = "1.0.28" @@ -253,16 +425,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -295,6 +457,20 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + +[package.dependencies] +wcwidth = "*" + [[package]] name = "pycryptodome" version = "3.20.0" @@ -336,6 +512,20 @@ files = [ {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, ] +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + [[package]] name = "python-dotenv" version = "1.0.0" @@ -350,6 +540,24 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "redis" +version = "5.0.1" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"}, + {file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + [[package]] name = "requests" version = "2.31.0" @@ -385,6 +593,28 @@ files = [ [package.dependencies] requests = ">=2.0.1,<3.0.0" +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tzdata" +version = "2023.4" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"}, + {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"}, +] + [[package]] name = "urllib3" version = "2.1.0" @@ -401,6 +631,28 @@ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "vine" +version = "5.1.0" +description = "Python promises." +optional = false +python-versions = ">=3.6" +files = [ + {file = "vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc"}, + {file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + [[package]] name = "werkzeug" version = "3.0.1" @@ -421,4 +673,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "95e9728a1492258abd1459823347f2a4775e15deea59c39ac1cc06052fe21c61" +content-hash = "52a8819fa1ada0e76b575b691fdc306fdca4671d11b3aa118a6f227a1535cf64" diff --git a/pyproject.toml b/pyproject.toml index 496cb50..5e15bfe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,8 @@ python = "^3.10" flask = "^3.0.0" python-dotenv = "^1.0.0" lark-oapi = "^1.0.28" +celery = "^5.3.6" +redis = "^5.0.1" [build-system]