From 36091e7796796fe06cbb0675d23b42048b148ff6 Mon Sep 17 00:00:00 2001 From: hackrobot Date: Wed, 17 Jul 2024 19:29:51 +0800 Subject: [PATCH] update --- app/__pycache__/crud.cpython-310.pyc | Bin 1800 -> 1787 bytes app/__pycache__/main.cpython-310.pyc | Bin 5319 -> 5898 bytes app/crud.py | 13 +- app/main.py | 202 ++++++++++++++++--------- images/combined_wxid_4413224132412.jpg | Bin 59416 -> 59250 bytes test.db | Bin 24576 -> 24576 bytes 6 files changed, 137 insertions(+), 78 deletions(-) diff --git a/app/__pycache__/crud.cpython-310.pyc b/app/__pycache__/crud.cpython-310.pyc index c7bc77ea863a3d5caf14352a16d8c3e283f2de6f..5cf81a5721a3f07fc46658ca1baa06510dc3216b 100644 GIT binary patch delta 182 zcmeC+`_0Rj&&$ij00c}q)6=BcHu5cHRMvC0iU}=FEh>&lEy_%eaY-%CF3B&5Nz6+r z%Fj&6FikAT+fhN+ox0pmgj7lv5A7^Yh0T9z7?6oyj9B9RiN8payt z8kS~eMutMR8iobT3mF(0fHX@D<3dIti!qp?h!bdtChIMhvc#g|$;VlBnSqSS-&vIf Z)qoPkd_Y2kk%N(kk*Q&_I@<$AZU6@ZF0KFo delta 195 zcmey(+rh_|&&$ij00hgwO-bWp-N?6;QPafPDkiizwWv5IwJ0+=#wE2lyClCLCbu#^ zEwQ*Hu^=560d2 zC*;#|vwZ9omfz}iw}#}F6(-&Ab@s8QE3;mgm)T&*$2;>-XMUnH4|nE~&iwJtoa@XR zy`&uVM6p+HmD^s4onrZITE`f47;?L3d5@oBMIm>1V{#`3_=}d@C3h2VGrwIk?^$6T zMke=q! zu%Faisq?W8*F=YnShMNLrS%harM!wi@N~iV1_~89@O)|B+pD(V#%d$Lk}V5$RzW?Ds9n=uIyglH-M} z%mY@oF&vCIhKb=~BruY&rrltH(Mknu1yaD$D~6uC+7z6F=bMI~Ir&+J9lh60JkVs4 zrk4z99yU%G+1O3t7k7<`NkWoZ~Sug()U)+U6@uL>@`Ieneq#@<8?B}JvpKw`NE7>%gv~2ePN`$Ff%P6ap$7~6l(%#um2rL zz*IaiBn#N9#(BQWXc)dxW|B)GO&MDj4H1YIa!C7Dl!dgUjf^t_Q8uL`{io}DUDhPC%YW)Gi18Y2rLAZU3S~6B5yc> z)k*|*;H)5hC4vbGGEfi)Jr>0Eg{(3#*)4nIx@7@{NrYirbvlSsXa#X(Uhm|RwV$G5 z{2IfP!@10CJj<`W4O|@x|CQO?bBzp>WqEtCUYINSC$nAQW8Jeka(~v1L|x6b!y=>2 zI;CwqwNUV9^L$a|MQMH}pITJm+ub=9h1a_Wo9>KP@f4zVQ7z(-8pV)lrCCO}NWe5p zVsP3hwnK^{ly%9(e`e7JG_HpmziK>!OhC+>QSZPX~ zD79(ballSr3S%wf-@-x~4VS!4RWaa!2v7rk(K1(zQ!GFgKxQX`xOy;%x2WnlL1G0) z)|HN*=MA_^isM|3`F1;xeMiOu9U)BwPSTGD$y4mG0X7G0p9Y)uiFBcMjrAVR1UQd^ z6O!VBNQ8@MvPjMKASlWGSZqc#E5ss68?X*I9@;Wb?)I;I&P2N5)NfH)5VAS0hA zT-)*pFfDrJo8MYJ{q4IYJG2MD(?+jc`Z0>q)!%<-_4N1Oee+UV@K?WEJ#%4AunBjJ zk6&pMwWLNnl9zt7`r6kasRrpH2}R{Ba;XSS=t>s}lpfUwsu}{wJBR`8Po4ekqD0Z5 z=wwAM&8F^-k)54~-Ce8H=ZIeyqL&EU43LlOvZ@=%3t=Jg&~yvb+GhaF1ZvaT&!yPB zZpPCLaVjW6Y0tVvIIZr#cDecjj$5*Gq^g!v&xEfh4p~P*Q(p{kCi)U>l-3X93hVk@ zm_h1#U;8ttH7ceVuBO6glH*M(oH}X|w}?^R-~pFL-B7*M?RD4){C_dep?e`7(dP-v zT9X%<`VwU8+8;ms$$LNlQM-q{`{r`H&qQMT=;%crnbTG2>2?~4ZKI#f{u$4o zs)v6|uHUB%gV4uy3Og9oz#4djuwj6|V$>)J(^SH#Cv!C%ZcBZ6&kTsAv#3W)Y4ZF) zrRon9k-9R=qeRUw`1RTp-6L;F&o7kbviwd9c_)?KLDKivT*59D_nHGhnyKWdQn)|; zNOSEU5_L3fM^txhM^t)k#|)~!A%(V|2@cp7wSiGLXj5TJRe-cQMqrwN2e8)Uh)`}5 z$Z)C%5GCph2r}=mc5Bvb+m5*I|BU0<{U=5(36MrrbOkrlh&e?&PECGIUJ z_hBZrF?4g5d>`<(#rZ8u@SB#&|7XSc2bRTeST_GJ{t#T}@#Oo?)8BZur1-- Lg09<~XKwu;dyVhk delta 2401 zcmaJ?U2GIp6rOwM?#%4$Y`bhrDHO0RwK9};+3kM;`3DgwiG&|F#%ArEZFgyR+q<&@ zbvBKu5@M_m4mK(@*+w70#0SNMH{+W=7-Hhvz8MAO#YclAqUYQ$r99wF?z!jQ^Ub;U zp8K6M?{$4|cjUuiQ-LRZb|L*~lcM~JgVj$x4E91ReDhVuTf}hIlskCa67jzy>t=)e zd8gKitXFt@wSS@7Z>{!rwcl3lH&y%1j>S71H67*KdFMNUIg*S^&s|G|D?H|C&bB#{ zR{0Jm!Q;>XpVoMScS~%y#P-ZCkt&ezUZ+!n81Li#64V2rUOuo2Ggx)R_z>SIF@1pP z=RK=1=x3KxmmR1sxtqTzk%Mx{J%H4?Ik&uS3rSOsC+&m?;0_tz%lAq65I0XL_WoNx zQJWI`)z@rkEH!8cFs2`XaFsGyQcBdNoOpDO5{1)}0wdK^aO5fCkPEs; zIGd*>)l)?RtN~AzRyE76DO~f^Yyf)Qqack%t;Af-V{-(&DW}j|Q0iu@rvM-%Vb)^c zX&6H7u%FY{+sRG&{^N)DU3jHCF@E;Q^x(OZQ@lTU@mTyq^5XPBLTrVdi_IYX&-KoA zb)e*@oLq+AyZ*)9cW?di_SNlouix;0)A^RbGWI!uRIJgW5Khh=aWj)n(kK=jaWt3C z`xD02V;ccrP?Inf8nGxNCfPtNV$di^_LqpG2ZrB_Qcxd1Dkugt8PIW1W?KATj4iJ< zoK-kE1>q}e9$A7wfTk}gM1grYCsSPGQa6FIv;@)Onw(&mUX@>7P;5F}tObw1U%hef z%I&+izrOqB$8an=_$Zu}5&CaEG)6Tzdkvl$UX9Ry#2`RsSPXoNPXF8BhFQ^z zPP#x;!fBClavW?J%}NB98WZ_qj*qyh!ui5z5Kxbw5GXj7QF;*+SIH72l}A0*V_v}1 zIGNW=LC+9tJ)=y4a=I5>QZYKf7`jMHrpsnMldJO(R+|mmy66N)VhF`f6f1iZyKpEu zvIVs^5OzRdEGy<|xWNO$Nx8rP*u31VJgC48+M)mR3L$$!C$Ssw$sjUfgs0q;X!o;` zjqwqfEi{7ALX;dD99>Wg{vHPZfKXSBfAIypUAZ#ik&?SRojyobD zaCOU8|IC^#;fr7_-~~shIN)DivzxT~ccP8;wUA^K+2c_Nk2-vlh=U(SH~Br$wp~>M z9E7i`EI_S!MYN!{9YhIQ0ohzbfdh~OV!OW(eRIPRn7V(JD9Xbc3}16D?>49Ls&D%3 zb(=}TKU8P;BIw!fkAw9xv=#RjP)!{7KdWmou_#GqB>X#djsCr8bQWnrcwkf;{>{_go;yeF^&u)SmpT<`RrW|gB7dd?D9650oee7 z#_1U=Hs<6Ufexpit^vsIl{5Sa15Qd53MbS#4*fM{k@jPW1~`(p;^qd)g8y=~9FFk^!k> z@LH+qSxBaAuuE)1TK#F0-UnAFu!Wt&b2Rw6F?n=*s%IkPN zUpo((=vw9Q^73wt2Y(@$sl^_ys9|`fY20mCk*SnBo{Y#9d?y?uN&lm8+rH4MoBIS@6}Ew3 z1_KI4pynYJE$E^4gEZALcB<3AsbM`Z8rz7$HWI^trfaJ42x~c|)+58*M*ahV#|_Q^ diff --git a/app/crud.py b/app/crud.py index f5ea976..b2be984 100644 --- a/app/crud.py +++ b/app/crud.py @@ -29,9 +29,16 @@ def delete_user(db: Session, user: models.User): db.commit() return user +# def update_user(db: Session, user: models.User, user_update: schemas.UserUpdate): +# for key, value in user_update.dict(exclude_unset=True).items(): +# setattr(user, key, value) +# db.commit() +# db.refresh(user) +# return user def update_user(db: Session, user: models.User, user_update: schemas.UserUpdate): - for key, value in user_update.dict(exclude_unset=True).items(): - setattr(user, key, value) + for var, value in vars(user_update).items(): + if value is not None: + setattr(user, var, value) db.commit() db.refresh(user) - return user + return user \ No newline at end of file diff --git a/app/main.py b/app/main.py index b3cebd2..6c64f2b 100644 --- a/app/main.py +++ b/app/main.py @@ -12,10 +12,9 @@ import qrcode import os from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles -from app.tgMesaage import sendTg #发送tg消息 +from app.tgMesaage import sendTg #发送tg消息 from datetime import datetime - # 初始化数据库 models.Base.metadata.create_all(bind=engine) @@ -56,40 +55,106 @@ def hello(): response = f"你好,\n..........................\n一名开发者\n副业:跨境电商\n#公众号:异度世界\n..........................\n回复关键词,了解更多:\n1. 微信机器人\n2. 数字游民\n3. 加群\n..........................\n[该消息是接受好友自动回复]" return PlainTextResponse(content=response) + @app.get("/tgMessage") -def tgMessage(openid: Optional[str] = None,db: Session = Depends(get_db)): +def tgMessage(openid: Optional[str] = None, db: Session = Depends(get_db)): current_time = datetime.now() print("Current Date and Time:", current_time) # 发送tg消息通知 wxid_4413224132412 || o7LFAwR32hWGq9XOpd7ZxK1wZxq8 - - openid="o7LFAwR32hWGq9XOpd7ZxK1wZxq82" - print('openid',openid) + + # openid = "o7LFAwR32hWGq9XOpd7ZxK1wZxq8" + print('openid', openid) if openid: # 查询用户信息:昵称 - userInfo=read_user(openid,db=db) + data = read_user(openid, db=db) + print('data==>', data) + + userInfo = data['userInfo'] + print('userInfo==>', userInfo) if userInfo: - print('userInfo==>',userInfo) - message=f'{userInfo.nickname}/{userInfo.gender}在{current_time}进行了访问' + # print('userInfo==>', userInfo) + # message = f"{userInfo['nickname']}/{userInfo['gender']}在{current_time}进行了访问" + message = f"{userInfo.nickname}/{userInfo.gender}在{current_time}进行了访问" + sendTg(message) else: - print('openid未关联,在') - message=f'openid: {openid}-未关联,在{current_time}访问网页' + print('openid未关联') + message = f'openid: {openid}-未关联,在{current_time}访问网页' sendTg(message) + def create_user_task(db: Session, user: schemas.UserCreate): try: existing_user = db.query( models.User).filter(models.User.wxid == user.wxid).first() if existing_user: # 可以选择更新用户信息或返回错误 - # print("用户已存在") + print("用户已存在create_user_task") return "用户已存在" - db.add(user) - db.commit() + else: + user = models.User(nickname=user.nickname, + remark=user.remark, + wechat_number=user.wechat_number, + wxid=user.wxid, + gender=user.gender, + friendaddtime=user.friendaddtime, + openid=user.openid) + db.add(user) + db.commit() + db.refresh(user) + except IntegrityError: db.rollback() - print("用户已存在") - return "用户已存在" + print("IntegrityError") + return "IntegrityError" + + +# 生成二维码图片 +def generateqr(url="http://nomad.hackrobot.cn", + wxid=None, + group_image_path="images/group.jpg"): + + # url = "http://nomad.hackrobot.cn" + # url="http://192.168.31.219:10086" + if wxid: + url += f"?wxid={wxid}" + + # 创建二维码 + qr = qrcode.QRCode(version=1, box_size=5, border=2) # 调整二维码大小 + qr.add_data(url) + qr.make(fit=True) + img = qr.make_image(fill='black', back_color='white') + + # 保存二维码图像 + qr_image_path = f"images/{wxid}-qr_code.png" + img.save(qr_image_path) + + # 加载原图 + # group_image_path = "images/group.jpg" + + # 在原图上添加二维码(右下角) + group_image = Image.open(group_image_path) + qr_image = Image.open(qr_image_path) + + # 将二维码放置在右下角 + qr_image = qr_image.resize((200, 200)) # 调整二维码大小 + qr_position = (group_image.width - qr_image.width - 50, + group_image.height - qr_image.height - 50) # 距离20像素 + # 在原图上添加二维码 + group_image.paste(qr_image, qr_position) + + # 保存合成的图像 + combined_image_path = f"images/combined_{wxid}.jpg" + group_image.save(combined_image_path) + # 压缩图片 + group_image.save(combined_image_path, "JPEG", + quality=10) # 85 是压缩质量,可根据需要调整 + + # 删除二维码文件 + os.remove(qr_image_path) + print(combined_image_path) + return combined_image_path + # return FileResponse(combined_image_path) @app.get("/add") @@ -100,21 +165,46 @@ def add_user(nickname: Optional[Any] = None, gender: Optional[Any] = None, friendaddtime: Optional[Any] = None, openid: Optional[Any] = None, + keyword: Optional[Any] = None, db: Session = Depends(get_db), background_tasks: BackgroundTasks = None): - - user_data = schemas.UserCreate(nickname=nickname, - remark=remark, - wechat_number=wechat_number, - wxid=wxid, - gender=gender, - friendaddtime=friendaddtime, - openid=openid) + response = None + user_data = schemas.UserCreate( + nickname=nickname, + remark=remark, + wechat_number=wechat_number, + wxid=wxid, + gender=gender, + # friendaddtime=friendaddtime, + friendaddtime=str(datetime.now()), + openid=openid) if background_tasks: background_tasks.add_task(create_user_task, db, user_data) - response = f"{nickname}你好,\n..........................\n一名开发者\n副业:跨境电商\n#公众号:异度世界\n..........................\n回复关键词,了解更多:\n1. 微信机器人\n2. 数字游民\n3. 加群\n..........................\n[该消息是接受好友自动回复]" - return PlainTextResponse(content=response) + + # 关键词回复 + if keyword == '加群': + print("keyword==加群") + combined_image_path = generateqr(url="http://nomad.hackrobot.cn", + wxid=wxid, + group_image_path="images/group.jpg") + return FileResponse(combined_image_path) + elif keyword == '手册': + print("keyword==手册") + response = f"手册正在编写中" + combined_image_path = generateqr(url="http://nomad.hackrobot.cn", + wxid=wxid, + group_image_path="images/group.jpg") + return FileResponse(combined_image_path) + elif keyword == '活动': + print("keyword==活动") + response = f"活动正在筹备中" + combined_image_path = generateqr(url="http://nomad.hackrobot.cn", + wxid=wxid, + group_image_path="images/group.jpg") + return FileResponse(combined_image_path) + else: + return PlainTextResponse(content=response) @app.get("/users/") @@ -128,7 +218,9 @@ def read_user(identifier: str, db: Session = Depends(get_db)): if user is None: # raise HTTPException(status_code=404, detail="User not found") print('查询用户不存在') - return user + return {"code": '400', "userInfo": None} + # return user + return {"code": '200', "userInfo": user} @app.delete("/user/{user_id}") @@ -145,54 +237,14 @@ def update_user(identifier: str, db: Session = Depends(get_db)): user = crud.get_user_by_openid_or_wxid(db, identifier=identifier) if user is None: - raise HTTPException(status_code=404, detail="User not found") + # raise HTTPException(status_code=404, detail="User not found") + # return user + return {"code": '400', "userInfo": None} - return crud.update_user(db=db, user=user, user_update=user_update) + # return crud.update_user(db=db, user=user, user_update=user_update) + return { + "code": '200', + "userInfo": crud.update_user(db=db, user=user, user_update=user_update) + } -@app.get("/generate_qr") -def generate_qr(wxid: Optional[str] = Query(None), db: Session = Depends(get_db)): - # 选择网络图片 - # return FileResponse('images/111.png') - - - - # url = "https://web.hackrobot.cn" - url="http://192.168.31.219:10086" - if wxid: - url += f"?wxid={wxid}" - - # 创建二维码 - qr = qrcode.QRCode(version=1, box_size=5, border=2) # 调整二维码大小 - qr.add_data(url) - qr.make(fit=True) - img = qr.make_image(fill='black', back_color='white') - - # 保存二维码图像 - qr_image_path = f"images/{wxid}-qr_code.png" - img.save(qr_image_path) - - # 加载原图 - group_image_path = "images/group.jpg" - - # 在原图上添加二维码(右下角) - group_image = Image.open(group_image_path) - qr_image = Image.open(qr_image_path) - - # 将二维码放置在右下角 - qr_image = qr_image.resize((200, 200)) # 调整二维码大小 - qr_position = (group_image.width - qr_image.width - 50, group_image.height - qr_image.height - 50) # 距离20像素 - # 在原图上添加二维码 - group_image.paste(qr_image, qr_position) - - - # 保存合成的图像 - combined_image_path = f"images/combined_{wxid}.jpg" - group_image.save(combined_image_path) - # 压缩图片 - group_image.save(combined_image_path, "JPEG", quality=10) # 85 是压缩质量,可根据需要调整 - - # 删除二维码文件 - os.remove(qr_image_path) - print(combined_image_path) - return FileResponse(combined_image_path) \ No newline at end of file diff --git a/images/combined_wxid_4413224132412.jpg b/images/combined_wxid_4413224132412.jpg index 10bd2d9a6481caabf4854301daabe747e1b4a5c6..e67541408a49cbfede985228aac90837b323453d 100644 GIT binary patch delta 5581 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 delta 138 zcmZoTz}Rqrae_1>+e8^>Mz)O!OZeFs`42PjAKomeu#=zLnvs)1R8-keSdn4!CV4|{ zGcE=O21fo@4E(P)3pzaDpZrQ+3aH@^1OFc&|0(~(0A4<3W(H1(GBjaMsPGR4{vS}) OoE+@TqMWWpnaKdC9U{;G