From 0e3fde20608d3db3bfb74a784799561a3952d760 Mon Sep 17 00:00:00 2001 From: yyd Date: Sun, 26 Oct 2025 00:33:13 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E5=85=AD=E5=91=A8=E7=9A=84=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/servermanager/MemcacheStorage.py | 88 ++++++ src/servermanager/__init__.py | 0 .../MemcacheStorage.cpython-314.pyc | Bin 0 -> 2287 bytes .../MemcacheStorage.cpython-38.pyc | Bin 0 -> 1579 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 156 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 144 bytes .../__pycache__/admin.cpython-314.pyc | Bin 0 -> 1065 bytes .../__pycache__/admin.cpython-38.pyc | Bin 0 -> 820 bytes .../__pycache__/apps.cpython-314.pyc | Bin 0 -> 470 bytes .../__pycache__/apps.cpython-38.pyc | Bin 0 -> 378 bytes .../__pycache__/models.cpython-314.pyc | Bin 0 -> 2498 bytes .../__pycache__/models.cpython-38.pyc | Bin 0 -> 1690 bytes .../__pycache__/robot.cpython-314.pyc | Bin 0 -> 10469 bytes .../__pycache__/robot.cpython-38.pyc | Bin 0 -> 6117 bytes .../__pycache__/urls.cpython-314.pyc | Bin 0 -> 387 bytes .../__pycache__/urls.cpython-38.pyc | Bin 0 -> 338 bytes src/servermanager/admin.py | 63 +++++ src/servermanager/api/__init__.py | 1 + .../api/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 160 bytes .../api/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 148 bytes .../api/__pycache__/blogapi.cpython-314.pyc | Bin 0 -> 2069 bytes .../api/__pycache__/blogapi.cpython-38.pyc | Bin 0 -> 1396 bytes .../api/__pycache__/commonapi.cpython-314.pyc | Bin 0 -> 3768 bytes .../api/__pycache__/commonapi.cpython-38.pyc | Bin 0 -> 2377 bytes src/servermanager/api/blogapi.py | 78 ++++++ src/servermanager/api/commonapi.py | 129 +++++++++ src/servermanager/apps.py | 31 +++ src/servermanager/migrations/0001_initial.py | 67 +++++ ...002_alter_emailsendlog_options_and_more.py | 50 ++++ src/servermanager/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-314.pyc | Bin 0 -> 2115 bytes .../__pycache__/0001_initial.cpython-38.pyc | Bin 0 -> 1327 bytes ...ilsendlog_options_and_more.cpython-314.pyc | Bin 0 -> 1141 bytes ...ailsendlog_options_and_more.cpython-38.pyc | Bin 0 -> 821 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 167 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 155 bytes src/servermanager/models.py | 82 ++++++ src/servermanager/robot.py | 255 ++++++++++++++++++ src/servermanager/tests.py | 121 +++++++++ src/servermanager/urls.py | 22 ++ src/servermanager/views.py | 1 + 41 files changed, 988 insertions(+) create mode 100644 src/servermanager/MemcacheStorage.py create mode 100644 src/servermanager/__init__.py create mode 100644 src/servermanager/__pycache__/MemcacheStorage.cpython-314.pyc create mode 100644 src/servermanager/__pycache__/MemcacheStorage.cpython-38.pyc create mode 100644 src/servermanager/__pycache__/__init__.cpython-314.pyc create mode 100644 src/servermanager/__pycache__/__init__.cpython-38.pyc create mode 100644 src/servermanager/__pycache__/admin.cpython-314.pyc create mode 100644 src/servermanager/__pycache__/admin.cpython-38.pyc create mode 100644 src/servermanager/__pycache__/apps.cpython-314.pyc create mode 100644 src/servermanager/__pycache__/apps.cpython-38.pyc create mode 100644 src/servermanager/__pycache__/models.cpython-314.pyc create mode 100644 src/servermanager/__pycache__/models.cpython-38.pyc create mode 100644 src/servermanager/__pycache__/robot.cpython-314.pyc create mode 100644 src/servermanager/__pycache__/robot.cpython-38.pyc create mode 100644 src/servermanager/__pycache__/urls.cpython-314.pyc create mode 100644 src/servermanager/__pycache__/urls.cpython-38.pyc create mode 100644 src/servermanager/admin.py create mode 100644 src/servermanager/api/__init__.py create mode 100644 src/servermanager/api/__pycache__/__init__.cpython-314.pyc create mode 100644 src/servermanager/api/__pycache__/__init__.cpython-38.pyc create mode 100644 src/servermanager/api/__pycache__/blogapi.cpython-314.pyc create mode 100644 src/servermanager/api/__pycache__/blogapi.cpython-38.pyc create mode 100644 src/servermanager/api/__pycache__/commonapi.cpython-314.pyc create mode 100644 src/servermanager/api/__pycache__/commonapi.cpython-38.pyc create mode 100644 src/servermanager/api/blogapi.py create mode 100644 src/servermanager/api/commonapi.py create mode 100644 src/servermanager/apps.py create mode 100644 src/servermanager/migrations/0001_initial.py create mode 100644 src/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py create mode 100644 src/servermanager/migrations/__init__.py create mode 100644 src/servermanager/migrations/__pycache__/0001_initial.cpython-314.pyc create mode 100644 src/servermanager/migrations/__pycache__/0001_initial.cpython-38.pyc create mode 100644 src/servermanager/migrations/__pycache__/0002_alter_emailsendlog_options_and_more.cpython-314.pyc create mode 100644 src/servermanager/migrations/__pycache__/0002_alter_emailsendlog_options_and_more.cpython-38.pyc create mode 100644 src/servermanager/migrations/__pycache__/__init__.cpython-314.pyc create mode 100644 src/servermanager/migrations/__pycache__/__init__.cpython-38.pyc create mode 100644 src/servermanager/models.py create mode 100644 src/servermanager/robot.py create mode 100644 src/servermanager/tests.py create mode 100644 src/servermanager/urls.py create mode 100644 src/servermanager/views.py diff --git a/src/servermanager/MemcacheStorage.py b/src/servermanager/MemcacheStorage.py new file mode 100644 index 0000000..5610ccd --- /dev/null +++ b/src/servermanager/MemcacheStorage.py @@ -0,0 +1,88 @@ +# 导入WeRoBot框架的会话存储基类和工具函数 +from werobot.session import SessionStorage +from werobot.utils import json_loads, json_dumps + +# 导入Django项目的缓存工具 +from djangoblog.utils import cache + + +class MemcacheStorage(SessionStorage): + """ + 基于Memcached的WeRoBot会话存储实现 + 继承自WeRoBot的SessionStorage基类,用于将会话数据存储到Memcached中 + """ + + def __init__(self, prefix='ws_'): + """ + 初始化方法 + + Args: + prefix (str): 缓存键的前缀,默认为'ws_',用于区分其他缓存数据 + """ + self.prefix = prefix # 设置缓存键前缀 + self.cache = cache # Django缓存实例(通常是Memcached) + + @property + def is_available(self): + """ + 检查缓存存储是否可用的属性 + + Returns: + bool: 如果缓存系统工作正常返回True,否则返回False + """ + value = "1" # 测试值 + # 尝试设置一个测试键值对 + self.set('checkavaliable', value=value) + # 读取测试值并比较,验证缓存系统是否正常工作 + return value == self.get('checkavaliable') + + def key_name(self, s): + """ + 生成完整的缓存键名 + + Args: + s (str): 原始会话ID + + Returns: + str: 添加前缀后的完整缓存键名 + """ + return '{prefix}{s}'.format(prefix=self.prefix, s=s) + + def get(self, id): + """ + 根据会话ID获取会话数据 + + Args: + id (str): 会话ID + + Returns: + dict: 解析后的会话数据字典,如果不存在返回空字典 + """ + id = self.key_name(id) # 生成完整缓存键 + # 从缓存获取数据,如果不存在则返回空JSON对象 + session_json = self.cache.get(id) or '{}' + # 将JSON字符串解析为Python字典 + return json_loads(session_json) + + def set(self, id, value): + """ + 设置会话数据 + + Args: + id (str): 会话ID + value (dict): 要存储的会话数据字典 + """ + id = self.key_name(id) # 生成完整缓存键 + # 将会话数据序列化为JSON字符串并存储到缓存 + self.cache.set(id, json_dumps(value)) + + def delete(self, id): + """ + 删除指定会话ID的数据 + + Args: + id (str): 要删除的会话ID + """ + id = self.key_name(id) # 生成完整缓存键 + # 从缓存中删除对应的数据 + self.cache.delete(id) \ No newline at end of file diff --git a/src/servermanager/__init__.py b/src/servermanager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/servermanager/__pycache__/MemcacheStorage.cpython-314.pyc b/src/servermanager/__pycache__/MemcacheStorage.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..264ee583b16e0f754f7b05f2e536b720af5e4782 GIT binary patch literal 2287 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 a%}KQ@Vg(ukva1-x_{7Y}$XLV-WB~yC%P4^W literal 0 HcmV?d00001 diff --git a/src/servermanager/__pycache__/__init__.cpython-38.pyc b/src/servermanager/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb4faef852caa515fb7dd04669b704cac62a385d GIT binary patch literal 144 zcmWIL<>g`kg5$xjGC=fW5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!Hii&czER$^Xy zK9q9G$xqkKO)M@+Es7~lEh-1ko&sCt$Mf4zOmr3o}y_Zao-1 znRqmM(wi6V{sms;1Ww|~5Q8U@HtNy$wlt7n;w1Zr0()o|2?dLBO zudt7f9yLx7HRB{a3P}j$m#xlO*xF=?XzCN4TXwYNTiW)icKjT~uubW9+H3|%q;6r5 zk7VAnjH6>MxG97^m2o4kLK#2dUf#HjB|(!jW{Qk8)2Jm-9%5{x6^KJkg|U@bDlOuK zCn>5$tTc;awJw59Q)5i&K*u2qw1%)P%@t!Z52+Tf5%(catr$Mrs9&UTD-tKLE z*nj`&$GfdX#pN?DA?;w5%f+nx#`>njE@Xg$DgeA7yVa@ZbMjPQ^KX_Kpbn1nG@z~1 zBqhELTsgoMtrMO1bGq=DXxF!Zxx5*kZ3eMe;7K%>u4e3oIf&C=*aB#++rhfp#3$fH z?_^baIb$m>R!l+ijTTp0q2t7a9EQz|tObe%QN-4v#8{~~P0aX-yBa60M!;KP>2310 zGTN&dFQ4~piDZ}p$BcDq1{Gw@Bv&n=O(~Qdx{)SK$4%}znXNnn4icwFp37XrG|@cK z-hjkcAO{hK5K0JT08@cDQJRQN=sp*bl66#7P&Le0C<3LTIMg8*f-#ue(y^Z~5=hw1 zi~?CjM}z_R6ejTJB~-Orz1yBU$T{_D`{D00b!rC$pjRkb&wGw3N7>XgFkhH0iLc8P zAEXf%S&w={l3jq@^qe!^-Rr$^z9BDx0<%$90S+um={Itr>ry)XdUj`Ye0y}fOW@FT Ui?sH}*%_PM9-I6{;E+Z83-*r!3;+NC literal 0 HcmV?d00001 diff --git a/src/servermanager/__pycache__/admin.cpython-38.pyc b/src/servermanager/__pycache__/admin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f7c0f7959473873b9f13f89b6ac07d5c27efe4f GIT binary patch literal 820 zcmZuvy^ho{5VjL%6ZUrRU`0tulY%V}6+-BaJD`DWRy5H`mdW_`a&nSA+l#cc+{^GJ zwA5~?;1Q^paTYkVSn`+o&CGn`chl*Z;rX)saru=q_KlR=N1$?y*F8Z12HdcgSDdrg z3=&9h7^IqCC7;qNMT>w8E!oZ@V63Gga~ z1hdmHJ?-08b>L1Y9SxapywM@A2YV<$yV^GI^`aD^kg`*)mNJZ`YgJv*86jCz2vT6EPFN0I{~c*Z=?k literal 0 HcmV?d00001 diff --git a/src/servermanager/__pycache__/apps.cpython-314.pyc b/src/servermanager/__pycache__/apps.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20c51d068a4a9d5ca8105c62c9a1dcedd8a5bd85 GIT binary patch literal 470 zcmY*Vu}%U(5S`@?L=cEkVuFPsHeO69jFmAaMha~xmKK<-aNrrZJNFh7I|^fGZ?w|R zhTbpm7tAJBh8V0&AlB}3N1{`_d9(B8Wp=i+nG~pat$v-^Du3u=9`T4wwiFpa11LFx zV;Owg%LwlU*a!i5N9E!HSP{X%V@2==(>W+jct5P!J-Cib+9{0n|Yd3L@4p zg+YlhX-veap?plaQnmS|&3nvc9h-+%G86oLe1?jZkV!{cuNpR4hZLTQlF19^?9s;h@%s l!kl6qLW$);ip3A9=C1qXF@z}WxLO$!G3_9(548xv*mfce3V_&@6C1FH>X;TJlx%-^oT3%~2Li{Ze_@b)-o#(x3S+F7Rn literal 0 HcmV?d00001 diff --git a/src/servermanager/__pycache__/models.cpython-314.pyc b/src/servermanager/__pycache__/models.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ed5a0c5a4e486f29be603e07d500c7c0b00e218 GIT binary patch literal 2498 zcmcJRU2GIp6oBvS&hAdz+1YlxW%(&v5lUK%^}$A?7BpHAL>e+QJW(dY&TYG9b_Va< z#e&4FCi0VLDv<;t#31njUK$<@DQyhA`P$S?x|zf$+v56UY!RP~=ge%!-9lqx;v_wL z@0oM&Ip^MUzG;sVZzS;iaOBsq10o?m<7D-TR0qo=FesAaL=_H@{Q?&{NE_Kk)KHqJ z;bGx0tXuo^g?$YVdzKCI71WR#emUgpd30SnQH|8t#n!cp*6k;OU9#RM&cJU>mDGm7 zZ{xaljbPUl*fptgnsC`Wx;Px2(zhCUTupH0DB%gU84jz(m9lm|uUm#wkip^NrSBJJ zzFoR;zH;+wWfz>$ZNY8K>*Jb9tx-PqC>q#QW@CiZu9=wl<^G*V-X<5WEzV3YUAbGi z^4$<@AnOHgYnJ_irW=O0Wa0kg(&c-;1>BUhYLEqw!GmKc0%>_O42t9^IUKzDXRsY2 z90VT`p5rEkf#^_k#KE;YSNcE>yn1ZlJ>43$t3QjMf3-Myv+~Wohu0>LIh38E4AR#} zDLdxZOYh0kZcNi0&NR(!UOk=Oss{60Mo}c?MC-(vi)Ut~P0KJRb#=Cy$8yFq4XP}` z!>Yi=VUb7F5Ra;1xC7CR2VM?QuCoSMbDIoy$6QI%EIm&lkVZ{|@Cqi5Wlej(pqte< z2~8WxF^8Kui&{3mhLld&4)vD0t+g}lq*-9P>9%1uI?rV_opY8uQQ*|kG}aD|*(MZi zAY9RlW(y1x@aM!qoFnCQ#|62Zd9EaF@lAJt+FLo;-ORS)1aoE`D4s!qjqEnOHKwzD zIcgdV>)ef1Q+6eTI#~#Ty72*>)1h3RdS-Aaj6#UErE$=?&8F^fJcHcGX-~s8;O3t9 z?YLu$re(p#F>+a+rKYKAPz~`KCFG3P@`jfjLlSrb2aXNm&+~Yxa&lg7pOf3K9-j)A ziC_3lJZLN?Q)`RKG89k=(wRwy;w@TUff?!ZJtkc&850-WgZx% z)WPDRa(e50x@Rukb8GOyP$~V+teE;;6e61+M?;aG#{`6bdg#5basopA1#jvImHOWZ z)eNrIJV2gkU$u&P15hWm#sH>I(5VURHa^v^Q3WCebc(CMB(8XLQh-j0fKEUq-VAhV zQCpxhlCHcrujkAo)H2?+M+?{(OPB91%-mg=o@U4*1`zToRQY5Q^gg*5BHROwuU$O% z>Eh(Q0xozs^X1al*Y^Q*Vg?=23nphxpdMn_)BgoO3=faBplAi*lgBSHEKoO9o53nY zu%{TFlvf0G_aBf0E4$|Vl&K|0uYl~}F;#?9inuLxKCo*G*s;~*CoWZrADi~*8Rhx9 zmZ~`QoW-Vh5F3byE)Nm2(w1^+$82loXVMd> zNIunT7(H}?aAOoNp4&bx8iwdN_pt!rusF52E5B*mCeA%$f2Z5pI{BQb>In4k7 literal 0 HcmV?d00001 diff --git a/src/servermanager/__pycache__/models.cpython-38.pyc b/src/servermanager/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b44f1b6331b81465f829e23beb67da286675205 GIT binary patch literal 1690 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 literal 0 HcmV?d00001 diff --git a/src/servermanager/__pycache__/robot.cpython-38.pyc b/src/servermanager/__pycache__/robot.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46f30547edbb90d58a34b0c7a7ade6c864505171 GIT binary patch literal 6117 zcmbVQ|8o?_m7kuSoms6`UjP9z_zU>^%6JXVw~LNL2v9JI5gby=&XmH|vOP$<%zjDF zEC$ue1qd6%8JpBOHo>H_n*(2TcEE`pL%_g~`4>|2YqYCXe#sB_Tk5LrKCfqXkrq;w zy4~uY?tZUdzkcuad$0S4U0qQHzdw%sWol@xqWqaUOMf~#+fkf4QxvXnW-F%uWhRq- z)l}s-WQOonZ7mlz!;El3ww^OgL$)ZnImq4ZA1T zYxc@^#9onGX|BwzGFPGB!J~FxuFvdaie*?VySlQ5cUD>PB9HMd-hEy**IIqEirH`V zXCDzat$u55b{+58s^qr;dwu0mSwCilvyb!Ms#;~DZn4S}?W3R`a=1$W38<`YpIpzcwd!O8DGuUoK|L;`Fv}9t)*t;v>xy0 zk4V@+3%0HWThAYruoqge$6Bz*`4bYhsRi56f^FnaO4y4n*i$Xo)BG6;OJFB2@n`vS zvbTA+!k_2y(+ZEzs^%8-2KWo;y@1}!oQ)~*O^aj!qjBa%9Lsex`AG+D<2CC*VMoD@ zt6tZza5HJ!at>HU`?zNWT_AOSFJoI{Zb76bEp(%kmYZ~@QZIk&+eBf9U6>p$W8XRnn@r_xhRT4vv_?+=ge-j{rB2U?I%Hc($8wxZA@B8!jf{=$**BUT8{c4oo6#DS7F>Nr_O|!QdV{; zKYnFKb3+x4Yh^7PuBtO(9-h(5VX?Iw=ISHLjPB}y808SbXIUA&N~EfA{k%3~l+{W{ z*{Cw6{A68Og#rwcy^#Zd5Tgi0vf34;d^+V?>8X@(lIcR;wes%tv(FC=PPuMzVz5jf zXJRluH1X2JONoJr1VF|7WI0=$jC)F4gNQP&ZF!yCa?&DGbV)v;Om5N(m4qFS3Jsey z*B^qG60Vgi+E~m@O*#o0Nm5I!1cs*y%QFsV@;qhRo_46fkBczIMIQlUr1HZh+fIU} z*O}rxneweA9`PbU(-A8OH!R40e4<*h;JBs+ZX8obO|y*veNGV@f&aIccMVSL%BJ#@ zh2TR5^r*G>>A~8S(#N~F8Y|l!zc<&Kp*`Z8xpHPHck3({-{l5 z48ge|To$)5@PuoPr{a!2G13bK?`F=O`lw3`xlJk8EHt{2Lsic+9)!?ysVc}$sOcKCCMpe`(@WV_-qFd|4t z?c33P+${h(y{HsbXT8v)##U0Tif>|Uy6YgFm&t-SKH&^Jy-a(gIWk(Qy{FM|l?tmu ze;m4VLv+BQBXt&jdj|R{ht`r-!qCd5e?%eT8IXiZH1aCqIds}~AmSJsrttw()9ady z{ylA+Jwe)#8mPVV_QG#(FMNEhcIRh*{rXZ|6A6$oSJJ_2l1Y-dA@yt_lNXx-1{NJT zK@#&inrM!Afd)6WD&JO`;B6R}R;H*C*2lzm(Vy--D6BL*&qGBT0)`YR5o(IGtcrE6 zT2`f*hsq(CIKqrHb9J<|8kqGI&r`P*0Ozqs0X??&zJNAvfp^^@0I6Nj+!;s@7i zzxk+s;qtc?uT2(NLcW+uA3-vyb1PlomKTw*{NaMvF>)+z`N?JUwPQcT zlsy=Z8=mG^_Tl^F#4c>e(=s%NSz`yt)8wJ6~ELYGGZ)c$gPzegG|+aKVovI4>q>SG2>zi$+C9C# zjpe17%lIW;=_pEm4Lb*e1Yw$sJ04s>!a_Iz45!R^h#r)w(MxE6aGhfD2kN>S-gd?-`e2Ve!O? z+L>GRcP`Y<{n85$@7lX(bX;a@jl2I|d+X}Lwd%r!w`)JWSiAoALgl0x+Vb)@N^~z- z{I7QrX#K2aQ{(y#A3exv-ubX`;;qIfC+lbbvr&C_;nHb;eE#--fX1MYO2I8>Elma% zN%uxJY#%hY<$n?ZbAOs3-2tH@-Ci0{Fe`}Go9nfAS`(yT=KmLWxON>(SZr3 zh8w?&fF&Ig9WPIp4uH2EGa1L;1KinxN?{ST4?dETUAo@Vm%OEpZ@=GH?~fC-k3Gt| z)ouxku$Vf%rtNG7OpeAoJa}fh+9Z=+G?~m5cnMD~NiUX6{-l($eTdjgtVW8W00Ftj z#eQ_f0ji{&+Jdh17*7&(JE{_jLka=kF_lF_R3e%mAu+t{N5bBYA_GKw2{gQKydPwxIa!I6Yc{wqBl7YE<>xlr)Z*eGnD!wlv+RRrdG5|%G++6${o`K8vBwp)0wcv<; zq9EOMCznqq30&C^2Zk9ufeAWRUh_dLLAUsf<;Z6s`J!X44-gZ-CAtH=~l9u^nmS<|E5qV-k20kdH|C3X+7m{y}&e$DxoX3^IB_X^)`n)HAI7(Tt#{ zDjW;E8PVgN8O>R_fDtoB0rzMsW7ET*r_(+&lb%+@wE?l}7~O;gJ$rbO9m8WI$z6L6 z&;vuRfM=lut#XiH9U%THVCVKYB6(Iuoyx_4t@*ppzQG6*Y!`FHT|e+s#x({a+mq9Xdw) g*&5a>+c+01yIC}bhYua417$UeMg`a0aQE>40ei}m+5i9m literal 0 HcmV?d00001 diff --git a/src/servermanager/__pycache__/urls.cpython-314.pyc b/src/servermanager/__pycache__/urls.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96307d2edc49063b3ad307beab069bd92eadab3e GIT binary patch literal 387 zcmdPqch>9W>pd?t^Esn&3g800|+|*m#Ky5%fOHzyS zidQmx2C88AVgw{ytYTci=0d5by|bRKTl8|_&gZk&#h^GO24t~bLFFwDo80`A(wtPg zB0iu2Ag>l{0*MdIjEszT8D#D<=sabRc)%^y!10iUqeEh%<&4nF!pfIfR6Z~>^D{MZ J7x4g90{~B6X+Hn} literal 0 HcmV?d00001 diff --git a/src/servermanager/__pycache__/urls.cpython-38.pyc b/src/servermanager/__pycache__/urls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..22ca2cb5124e1d5f2abbe01102ca3bc6c3338dbf GIT binary patch literal 338 zcmY*Uy-ve05Vjqs4K!JKhYXF>6;%kK&MXy+6{&LZDJ|G>WT&l|`!qZPPry5PWh#%r z0_T(gPP#vT_kDMMD2hvh_P+ewykY)`&0pi#+@kwA21OLr#85$LteIve5ee3b;+h*S zIM%61fvOQCz>oCE8Y)I=yd-6ILIqONz&qJ*Fkcw?46FSX4kvm>QfJq;4}}M}2WMm} z8*nZ~@V*>}iF%T4W2>Fh-Z6X>R|gmv)uU~Dw_R7~36~%^&_X7=Zq-TyVS+SV+=FZV zD{=AHL-?94uIt61{G0ByZKg(g9IgLdQpe3z_Y$(Xv1+H`Cf@C%R8Di2GtTnw9}DzZ AtN;K2 literal 0 HcmV?d00001 diff --git a/src/servermanager/admin.py b/src/servermanager/admin.py new file mode 100644 index 0000000..b8cdfe8 --- /dev/null +++ b/src/servermanager/admin.py @@ -0,0 +1,63 @@ +# 导入Django管理模块 +from django.contrib import admin +# 导入自定义模型(假设已在同一目录的models.py中定义) +from .models import commands, EmailSendLog + + +# 注册Commands模型到Django管理后台 +class CommandsAdmin(admin.ModelAdmin): + """ + 命令模型的管理界面配置类 + 用于自定义commands模型在Django admin中的显示和行为 + """ + + # 设置在管理列表页面显示的字段 + list_display = ('title', 'command', 'describe') + # 字段说明: + # - title: 命令标题 + # - command: 具体命令内容 + # - describe: 命令描述 + + +# 注册EmailSendLog模型到Django管理后台 +class EmailSendLogAdmin(admin.ModelAdmin): + """ + 邮件发送日志模型的管理界面配置类 + 用于自定义EmailSendLog模型在Django admin中的显示和行为 + """ + + # 设置在管理列表页面显示的字段 + list_display = ('title', 'emailto', 'send_result', 'creation_time') + # 字段说明: + # - title: 邮件标题 + # - emailto: 收件人 + # - send_result: 发送结果 + # - creation_time: 创建时间 + + # 设置只读字段,这些字段在编辑页面不可修改 + readonly_fields = ( + 'title', # 邮件标题(只读) + 'emailto', # 收件人(只读) + 'send_result', # 发送结果(只读) + 'creation_time', # 创建时间(只读) + 'content' # 邮件内容(只读) + ) + + def has_add_permission(self, request): + """ + 重写添加权限方法,禁止在管理后台手动添加邮件日志记录 + + Args: + request: HTTP请求对象 + + Returns: + bool: 总是返回False,禁止添加操作 + """ + return False + # 这样设计是因为邮件发送日志应该由系统自动创建 + # 而不是手动添加,保证日志的真实性和完整性 + + +# 将模型注册到Django管理后台 +admin.site.register(commands, CommandsAdmin) +admin.site.register(EmailSendLog, EmailSendLogAdmin) \ No newline at end of file diff --git a/src/servermanager/api/__init__.py b/src/servermanager/api/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/servermanager/api/__init__.py @@ -0,0 +1 @@ + diff --git a/src/servermanager/api/__pycache__/__init__.cpython-314.pyc b/src/servermanager/api/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f04b8ffa207b7c605d9c7f95c951c5e2243920c7 GIT binary patch literal 160 zcmdPqg`kg5$xjG8lpMV-N=!fCL?YxR?b`H?g=RwJ4@IwWut$C^s=LF&)TDEXa(BkI&4@EQycTE2zB1VUwGmQks)$ L2QuCGDwPa&G^EUe_Y3sgt%_MKX8yWx!E~-13Ek88nJ|{WKQ71Aoj(p;+({#Iho6Yq?-&8OX?w( zJSC=HT(OigF>_CFXbQ2m=-SL#e}Btm+lwwY#W;V3anJGHSbf*#?pmE~JE?1mLUV95 z105Uo|Cn&WB3!(dBE?Xj?f_w9vB?IOy1yHqe{FN%<1uGAyP~-eNetqrA zrsrf~F*+oSKlNxGIy+?P*fxb$XjM`>o~x3DW@@P|#Ra8xl=dw$)${_QREUB5B+U}w zFk(#RO_||jtOW@xH<6XV<#EB@;&qw~;|@wU>`y4SZ@5l}`rw@kRV*w}W;I?r0(R+&k+Dq)he%sZ4{oC*$dq0iy*M0d~lN z)){F-p*9q0vu_@C9Ft9Lex(JyABuFktRq$~i*%2&h5$cQvGM4=d4einL~LxTq047%Kt9xmC~SE<2%+V&;I$V>Q#mNwsvJ zAShcC_rH+8%#3#vOacIfM(P_hVD2e#;^k%C0uSb>Ysf9Au2fHPFoe`9QoG{mMp?GB}$E8+A zP8oY4R8US%|4=`F_x7FJkv<&i!~6P$#>n)&m8dcuR;C;JrG|Pb$=?*yo$-;+n{8Os z@RFdY6`*?7SGIiY7b%of+izNQN>@D=aNn!BwK}B?#6#Gt2>l2b5KvihH>Cl$;ZKU% zoHK8Ei@O1YgTn0ib`Y$14tHGNrxaAx-ZBYpDAKo+XT`9ZCV*us$?oq@;ld9^Nhu!| zBxU@tASji?oUC*`D+Yvmxbvh+thN05%U_m#)Q%Mkn--!8`n9p+E!pso?NZkGbdiS+I literal 0 HcmV?d00001 diff --git a/src/servermanager/api/__pycache__/blogapi.cpython-38.pyc b/src/servermanager/api/__pycache__/blogapi.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6beae7693fee46733db43888863cd139ea245574 GIT binary patch literal 1396 zcmZ`(Pfy!06t|r;4J8!V#?WA!Cbmls>uHB+nkE=XyH%R1T_PmQ#HOUA4cO_lR^179 z+;@N@pJd;`*PYl$*lF)24Jw7X@^frIzxVsSKmD?_)F3c^b^aW^bO`y4o7n+pWE-Yw zfsjPfl=NvtDSoq*^_|G+yOB%DCn6o`o)hVwv8X0%9TIxi@P>rcHakk__~`Rc=}|}J zA+z-zohNastiLOA)y?!MM%~%LV6zR=yaAy|L?wxsq>`PJ$SHkAuB=H9_BC0TKCGTx zkPTStauNIow*E2Ax;rOHOue^t6K2BHf)agyvGz%8t;laci~PX-B?tb-lnfgseU0UfVQ zX|^hb7;#nux!&;If(`H_7jyzVzq1LuV3!0}iRaD__5?Uz(Ke7p&Uyia_0vp9F49zE zwS473an}h;R{(=HWc(s+`BuO5ahux z^k)%E{o-~Ui~3>hkVefL?|dNo3ZjcC(Q6>AcbKG*{9CaNDt*+peig@gVr#b$S6Yvd z8)lEtCUs$spWVx_(!Ue+n{q)*Rn%t-q>TRz3H=x}3kLTl;34AFDpt@DmDde;QDtCr z7T=s+JM^*iF%X>F1~gbEhbevs9J)@V<&9oO1PEBKqu5086a}{3jKBibH4xZA2J6bF zehq$(?-l3m(6h}WF*3P`k6T5NmyN%80L|C}_bN4I{;>XvH#7DNcSRTY?@Op3@Gr-p IZopQYf3^rae*gdg literal 0 HcmV?d00001 diff --git a/src/servermanager/api/__pycache__/commonapi.cpython-314.pyc b/src/servermanager/api/__pycache__/commonapi.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1664c95673ed26438c532bf3e8fb543285b50d88 GIT binary patch literal 3768 zcmbUjTWl29_0D6T?8Ek8Y;V9`%V6&+W-$T>YXJvCcvG_-s1=fRM&sSFJ?ZYwW@Z)v zQKSum*bqW+K{W(KLe#2ut3awsg_4$tulCc%P1l)Fi3--3{?xFjAo|mDXLkGoHEOOj z=bkzD-gD1A@0UGpCxY?kJ^$=IYDMTS{xOH8h!lzLiMmt z)!SJDn(;(nb^hq|nv$Dy4QRhk|AGr1;O*_hgl3OP$t0hOE1G9_WOIAlcCIb5o!hy2 zpXS`QcW;D?u)AO17n{wzC@(5B0hI@tj1Dx zC`fCzO})IjHL^EWG^ftn29QF?UU&vb7QKX`IvInAdEqn#Xr^P$qOtHamSm|8w1Nx~ zEWM_9E=_b05pc};<1N(ao1sPQGq{Pp$Pw$1A4Of}4l6R;Ax%a-CM{BGVl(qza>fWt z!0>7ERg$%WF?38MM6{M#fmLLinpH{*DPGh(Snr#p zWI7?JqLk9CF4Ez#f-FmNkkYJa8ThE0 z6P(9nM+gcS5Y_wrkJrNdS~&XJ+kC1=Do**2KFMGH{K2I^+&_Og3Xj<@$l!J$x*X-x zVibF!lmaZ2KB&<#kW_OO{Un6{uuheC0A$e=ZFK~0SJmXILZeloTvhXERr9so<8532 zvS{STZ;V8{MmEGoUX4#t$oEPC5x#Dsf5#nWNsd`I$}Ibg|Aqe^YZ0U*Je`gy0Gi3qVGo=!4J>2GM^P-#H1#MyKrmwuMnY!6* z0el#>2kn}j<5GN5;5g06abTEC0^=@@dndyuiYs1@>lS52O^7KWCE;a7;LZykdrW9OXsHp8;e0?Tvad$AGUIJ#AuB>`Gk3ZRvx z0oquLYKJ&?V8;&Hz~79_vJJ9JLXhW#I}S}f+;;(z{Q<#lX2A}W0uIJtQ5_m4+Jlr# z13|M%U2h99RnaIukq8nRtq6&3T~YPAqOjS-l&FG+E9QzCG8dKrAKm_0bi2}@s|<`* z26B~+qm_+gm7#w6q+7;SAvQZf;Y0&3a}_=ZgeTx_ran;=$A>;Th*bpEVgmFP!NSfP zX8t#mMw;v(r|VB0t^{xnr_6by11jnFB{h>LBp(v&L}fMu6X)dDrzS6*$PfJK-dDeH zhSR*vC;h6ZCWNrxB+3G0q*T~H`SqFn`HKd1;@Gd?*!{0gPh7ma zdH{(5n?EAcI+D+!#{CAUIi-(HcfrDXzz6i zKG_xLTeYS0>@8+u<#wV0kK#W}hd>%*C(=0k$`)R|uhEJEDTYi4U&`++sIsE2t zI&b;Hy7@EBuxH}JuP2V($oGGF|NOQ5(cc-``PUy$OkTM$iv}`2$2Kc329$P!LdVdmHse|iY4Pl z!tM4O&!ga!R`&eM{BTCu2ms#k*oC|dq`BKA;qj7%gYE%$!9u!g3KZq__ft>o$mJbp zDsoK2DAO=R4Yhq?z2X`6j4^9QTxL5%9d{GTIO^+R3 zue8&38!9c-{KcpSLHz{7PghJLgWYVlI~v8W7FrEbQZ!2%*FUJE0w34h8+k>TE^MYk z*8u~KlA)AhZ>+C36e@=?xq6;L=NV@O>`}1PP?t#B5xiZta4_(*z}Z!&m*>isj+QMQ zE35C{G|nvQ-vMQ2@nb|df*KQ;qF{ce;6i(NSpW?!SVR4uSVPT;py`(AgwCEt(8#8pVBxy5bmie7Wap^taIwX4x3C@f+91Ai4RAol)m>XSr;`flO`&4g!2z1v zrT1;dL(B1gn~>Ts%Fz5#u&UXh-{O8M9E2E=r2_|z&OsLrdO}|9l2jSD4|rKjsd|qh zZ*)_TM=lss^ zeB9caLeT!``*ZlLhR|a~Z?1CO#2SE|VE;8bOY~W~43y~9>`p!bN4;oL%# zm!%8$LJvLzQP~or0xl4Fy^5$ZLGqZYmq`s*aY3Pp4XB_N5=T)VQcO^QEx!?XK;eVVMv5LM;c3XNgd` z?=q3JeJ^BQ2n&g#Pnj$9A`1d5&jK!AUS0!YcH(m5V!d(m$Hw^dlWR9;rq5+tgyxr+ zXE{bj8oJjnmRuG(z9%%BGb@yuhkeIpfk-wL@+7DdE&ET}tQ4Pg{d}HrkzkzrJgW$; z#6eFeQV^L1b(l>dz7}r&)3dK1MFH zRfa+ps*WMRR)l>c5u@G!yhDgnA_MN1NInSNWqb+bWDuq92y)VR3Ajm8M^$7-5ShZc ztit6Hk@N=(%nk#gSgs2hq6W+zT4s-QjpI3?X}$_q0(lSN%DSa5=vp{qNBi;&G>QI1 z^FMe81|bUZI2yu{nR*o!$S4`bHBu#fTNO3BfjtiL+N50K8;cKSM z8Nd)g9e5!c0yJKSZ$+;cie03!37h3wWQPLxI$##L1Ze~lNGSDR@>Eq-+)gTMRvNVe z6gu3G@YxLmpf+%571tw_N-#9(PXYSdijn;FM_k^dIFc*Nm@JNC%u}0yhKV1DL`hN= z5FWFrXx(E4>=_3wnD+)K5DEv)-5;agR zV-vT+fMVcxK+it(=|(URxW9m_qyVM@=PQ@Vd#Hwwr>e4CsX2iv`f0n?bDGvtfGunb zOtWenHf_iU&I{w{=#&LEcLrJ0D6l6eacS8yqlBJkp*hUl(z~!3Nbe)8LfF=?Oyq@= zxoncJld5Fd;~S*tkV3LmQoDW!R935q(IGtIKh zj>fs0GxyKT-5r0cySuyb^9=y?%=DGrhvvQ+o4+$Xdwo2+MkHm;aJ)S4gbQ2-MXX)I z{asNN3u6DY!1F(hicI`?$-^Sayb}(GN~OTANJ51$N7yMq3HMK)ipV*7oB;*Q3J3j= s%eo=A9WVS}numOj%fwDexWPscU@Fl`nxseur*THfv}QKxfF2e92cB7LQUCw| literal 0 HcmV?d00001 diff --git a/src/servermanager/api/blogapi.py b/src/servermanager/api/blogapi.py new file mode 100644 index 0000000..d9e4a7d --- /dev/null +++ b/src/servermanager/api/blogapi.py @@ -0,0 +1,78 @@ +# 导入Haystack搜索查询集 +from haystack.query import SearchQuerySet + +# 导入博客模型 +from blog.models import Article, Category + + +class BlogApi: + """ + 博客API类 + 提供博客文章的搜索、分类获取和最近文章等功能 + """ + + def __init__(self): + """ + 初始化方法 + 创建搜索查询集实例并设置最大返回数量 + """ + # 创建搜索查询集实例 + self.searchqueryset = SearchQuerySet() + # 初始化空查询,准备后续搜索 + self.searchqueryset.auto_query('') + # 设置最大返回文章数量 + self.__max_takecount__ = 8 + + def search_articles(self, query): + """ + 搜索文章方法 + + Args: + query (str): 搜索关键词 + + Returns: + SearchQuerySet: 包含搜索结果的查询集,最多返回__max_takecount__条结果 + """ + # 使用Haystack进行自动搜索查询 + sqs = self.searchqueryset.auto_query(query) + # 加载所有相关对象(避免N+1查询问题) + sqs = sqs.load_all() + # 返回前__max_takecount__条结果 + return sqs[:self.__max_takecount__] + + def get_category_lists(self): + """ + 获取所有分类列表 + + Returns: + QuerySet: 包含所有分类的查询集 + """ + return Category.objects.all() + + def get_category_articles(self, categoryname): + """ + 根据分类名称获取该分类下的文章 + + Args: + categoryname (str): 分类名称 + + Returns: + QuerySet or None: 包含该分类文章的查询集(最多__max_takecount__条),如果分类不存在返回None + """ + # 根据分类名称过滤文章 + articles = Article.objects.filter(category__name=categoryname) + # 如果找到文章,返回前__max_takecount__条 + if articles: + return articles[:self.__max_takecount__] + # 没有找到文章返回None + return None + + def get_recent_articles(self): + """ + 获取最近的文章 + + Returns: + QuerySet: 包含最近文章的查询集,最多返回__max_takecount__条 + """ + # 获取所有文章并按时间排序(假设Article模型的默认排序是按时间倒序) + return Article.objects.all()[:self.__max_takecount__] \ No newline at end of file diff --git a/src/servermanager/api/commonapi.py b/src/servermanager/api/commonapi.py new file mode 100644 index 0000000..98fbc0e --- /dev/null +++ b/src/servermanager/api/commonapi.py @@ -0,0 +1,129 @@ +# 导入必要的模块 +import logging +import os + +import openai # OpenAI API客户端 + +# 导入自定义的Django模型 +from servermanager.models import commands + +# 获取当前模块的日志记录器 +logger = logging.getLogger(__name__) + +# 配置OpenAI API密钥,从环境变量中获取 +openai.api_key = os.environ.get('OPENAI_API_KEY') +# 如果设置了HTTP代理,配置OpenAI使用代理 +if os.environ.get('HTTP_PROXY'): + openai.proxy = os.environ.get('HTTP_PROXY') + + +class ChatGPT: + """ + ChatGPT API封装类 + 提供与OpenAI GPT模型交互的静态方法 + """ + + @staticmethod + def chat(prompt): + """ + 与ChatGPT进行对话的静态方法 + + Args: + prompt (str): 用户输入的提示词 + + Returns: + str: ChatGPT的回复内容,如果出错返回错误信息 + """ + try: + # 调用OpenAI ChatCompletion API创建对话完成 + completion = openai.ChatCompletion.create( + model="gpt-3.5-turbo", # 指定使用gpt-3.5-turbo模型 + messages=[{"role": "user", "content": prompt}] # 设置对话消息 + ) + # 返回第一个选择中的消息内容 + return completion.choices[0].message.content + except Exception as e: + # 记录错误日志 + logger.error(e) + # 返回用户友好的错误信息 + return "服务器出错了" + + +class CommandHandler: + """ + 命令处理器类 + 用于管理和执行预定义的系统命令 + """ + + def __init__(self): + """ + 初始化方法 + 从数据库加载所有预定义命令 + """ + # 从数据库获取所有命令对象 + self.commands = commands.objects.all() + + def run(self, title): + """ + 运行指定标题的命令 + + Args: + title (str): 命令标题 + + Returns: + str: 命令执行结果或帮助信息 + """ + # 使用filter和lambda函数查找标题匹配的命令(不区分大小写) + cmd = list( + filter( + lambda x: x.title.upper() == title.upper(), # 不区分大小写比较 + self.commands)) + + # 如果找到匹配的命令 + if cmd: + # 执行找到的第一个命令 + return self.__run_command__(cmd[0].command) + else: + # 未找到命令时返回帮助提示 + return "未找到相关命令,请输入helpme获得帮助。" + + def __run_command__(self, cmd): + """ + 私有方法:实际执行系统命令 + + Args: + cmd (str): 要执行的系统命令 + + Returns: + str: 命令执行结果或错误信息 + """ + try: + # 使用os.popen执行系统命令并读取输出 + res = os.popen(cmd).read() + return res + except BaseException: + # 捕获所有异常,返回错误信息 + return '命令执行出错!' + + def get_help(self): + """ + 获取所有命令的帮助信息 + + Returns: + str: 格式化的命令帮助信息,包含所有命令的标题和描述 + """ + rsp = '' + # 遍历所有命令,格式化输出 + for cmd in self.commands: + rsp += '{c}:{d}\n'.format(c=cmd.title, d=cmd.describe) + return rsp + + +# 主程序入口 +if __name__ == '__main__': + # 创建ChatGPT实例 + chatbot = ChatGPT() + # 测试提示词 + prompt = "写一篇1000字关于AI的论文" + # 调用chat方法并打印结果 + print(chatbot.chat(prompt)) \ No newline at end of file diff --git a/src/servermanager/apps.py b/src/servermanager/apps.py new file mode 100644 index 0000000..8098071 --- /dev/null +++ b/src/servermanager/apps.py @@ -0,0 +1,31 @@ +# 导入Django的应用配置基类 +from django.apps import AppConfig + + +class ServermanagerConfig(AppConfig): + """ + Servermanager应用的配置类 + 继承自Django的AppConfig基类,用于配置servermanager应用的相关设置 + + 这个类主要用于: + 1. 定义应用的配置信息 + 2. 在Django启动时执行应用初始化代码 + 3. 配置应用的元数据 + """ + + # 指定应用的Python路径(必需字段) + # 这应该与settings.INSTALLED_APPS中使用的路径匹配 + name = 'servermanager' + # 说明: + # - 'servermanager' 表示应用的Python导入路径 + # - Django使用这个名称来识别和应用配置 + + # 可选配置字段示例(当前代码中未使用): + # + # verbose_name = '服务器管理器' # 应用的可读名称 + # default_auto_field = 'django.db.models.BigAutoField' # 默认主键字段类型 + # + # 可以在ready()方法中添加应用启动时的初始化代码: + # def ready(self): + # # 应用启动时执行的初始化代码 + # import servermanager.signals # 例如:导入信号处理器 \ No newline at end of file diff --git a/src/servermanager/migrations/0001_initial.py b/src/servermanager/migrations/0001_initial.py new file mode 100644 index 0000000..0cfdd13 --- /dev/null +++ b/src/servermanager/migrations/0001_initial.py @@ -0,0 +1,67 @@ +# Generated by Django 4.1.7 on 2023-03-02 07:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + """ + Django数据库迁移类 + 迁移用于创建数据库表结构,这是初始迁移(initial=True) + """ + + initial = True # 标记这是初始迁移 + + dependencies = [ + # 依赖关系列表,此迁移不依赖其他迁移 + ] + + operations = [ + # 迁移操作列表 + + # 创建命令表 + migrations.CreateModel( + name='commands', # 模型名称 + fields=[ + # 主键字段,自增BigInteger类型 + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + # 命令标题字段,最大长度300字符 + ('title', models.CharField(max_length=300, verbose_name='命令标题')), + # 命令字段,存储具体命令内容,最大长度2000字符 + ('command', models.CharField(max_length=2000, verbose_name='命令')), + # 命令描述字段,最大长度300字符 + ('describe', models.CharField(max_length=300, verbose_name='命令描述')), + # 创建时间字段,自动设置为记录创建时的时间 + ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + # 最后修改时间字段,每次保存时自动更新为当前时间 + ('last_mod_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), + ], + options={ + 'verbose_name': '命令', # 单数形式的可读名称 + 'verbose_name_plural': '命令', # 复数形式的可读名称 + }, + ), + + # 创建邮件发送日志表 + migrations.CreateModel( + name='EmailSendLog', # 模型名称 + fields=[ + # 主键字段,自增BigInteger类型 + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + # 收件人字段,存储收件人邮箱地址,最大长度300字符 + ('emailto', models.CharField(max_length=300, verbose_name='收件人')), + # 邮件标题字段,最大长度2000字符 + ('title', models.CharField(max_length=2000, verbose_name='邮件标题')), + # 邮件内容字段,文本类型,无长度限制 + ('content', models.TextField(verbose_name='邮件内容')), + # 发送结果字段,布尔类型,默认值为False(发送失败) + ('send_result', models.BooleanField(default=False, verbose_name='结果')), + # 创建时间字段,自动设置为记录创建时的时间 + ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), + ], + options={ + 'verbose_name': '邮件发送log', # 单数形式的可读名称 + 'verbose_name_plural': '邮件发送log', # 复数形式的可读名称 + 'ordering': ['-created_time'], # 默认按创建时间降序排列 + }, + ), + ] \ No newline at end of file diff --git a/src/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py b/src/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py new file mode 100644 index 0000000..67e8c70 --- /dev/null +++ b/src/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 4.2.5 on 2023-09-06 13:19 + +from django.db import migrations + + +class Migration(migrations.Migration): + """ + Django数据库迁移类 + 这是一个数据模型变更的迁移文件,用于修改已有表结构 + """ + + dependencies = [ + # 依赖关系:此迁移依赖于servermanager应用的0001_initial初始迁移 + ('servermanager', '0001_initial'), + ] + + operations = [ + # 迁移操作列表 + + # 修改EmailSendLog模型的元选项 + migrations.AlterModelOptions( + name='emailsendlog', # 模型名称 + options={ + 'ordering': ['-creation_time'], # 改为按creation_time字段降序排列 + 'verbose_name': '邮件发送log', # 单数形式的可读名称 + 'verbose_name_plural': '邮件发送log', # 复数形式的可读名称 + }, + ), + + # 重命名commands模型的created_time字段为creation_time + migrations.RenameField( + model_name='commands', # 模型名称 + old_name='created_time', # 原字段名 + new_name='creation_time', # 新字段名 + ), + + # 重命名commands模型的last_mod_time字段为last_modify_time + migrations.RenameField( + model_name='commands', # 模型名称 + old_name='last_mod_time', # 原字段名 + new_name='last_modify_time', # 新字段名 + ), + + # 重命名emailsendlog模型的created_time字段为creation_time + migrations.RenameField( + model_name='emailsendlog', # 模型名称 + old_name='created_time', # 原字段名 + new_name='creation_time', # 新字段名 + ), + ] \ No newline at end of file diff --git a/src/servermanager/migrations/__init__.py b/src/servermanager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/servermanager/migrations/__pycache__/0001_initial.cpython-314.pyc b/src/servermanager/migrations/__pycache__/0001_initial.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4b8c2b04e28959202020591394dd0ecc7ba156a GIT binary patch literal 2115 zcmb7EU2M}<6t_dk6sihk)M#(lyy&$K-=q%?Q}_{7HyVdpOgEY zd(ZcsbI;C5ILN^B>zO~V#nhgMLyJkxIv%0zSo2NfNotE&7%Mc z4nhX@1IWe=ks%abgN$s5p=w)fQzk*^Pz6k zQ_}$bXI&k&wt`w)Q7_t-Vt0FMnx#B6g0|Bpv;&&=bt`acwQEN$KCNZ5qAD4dN0yFS zp5sGFpA`~wRKsbVh?GcH+(3M z-{#h>Kb}0aLe-Qom9y35ukM!@9xvZ}v~urfIK@p|)jr0eB)R8=715v=?q)4$>B;Q! z-T8lmc!w(rZ6neh! z&GPr(0Z+d~CPetx!R72JaH0cg032c0#;W>$u;Vlp~IS{5K(o#pf*WXXKNeA z28B7B#v-NA8#zi0jP(Q@^a6g&%VBs8y*W1Yfv9G*>Zknmx8>OfD?iLXzdt($Z3`uZ zP8TyoAFGY+v4MesJ-Es&HF;gPJJEUBhVcos^G)ViqHI9*lS+Irm7d(4O5_{=;rC|#aLfH*7q>I7%9ZgnZo%}G&X&- z7~hqT?*jb6LVVN|-h+Ln&lWrS^Bw(jEsKW=9T!dE{Zh1RW>c|yf4+PF(|zVysnAVK zVWJdG%xo|A9LV<^cMgWUAkSb zOt+SqcX4MSe%2Ju)pL**I}5S%rf{L2L#hxzZVD$#(assJnAn?7?427nN6r@#7fj*e z`V?x(nu$G-t-0f1Nr+8jGu~hE`|PxvAnoo%GxzxkefMfLPQzFVNKScE(zw%fPM^8; zO4u+5I{=o&zvcGyA^i<-?Xhdb;Z^anEc-Xdu*sL)4wkzeEDF893B7+Y;5gC$0MaWo ACIA2c literal 0 HcmV?d00001 diff --git a/src/servermanager/migrations/__pycache__/0001_initial.cpython-38.pyc b/src/servermanager/migrations/__pycache__/0001_initial.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39e38ec6441337364899261b40c6c59f45ec26c4 GIT binary patch literal 1327 zcmZuxO>ERg6t>6Cu6Nf?LJ4gt{o$`j+ZL2w5vpidK$Uo7xJoM{HMV1VZ42yaqd79b zvqqE1dc*@kGoHk$tV9X0FzqPO@Z%vkj&E8w4TBkN6O1+ivuy=N+h!w+73G3e7acHh z*f0W>WqY1L758f3Kn?241aU)S7rYXWmh!O@k0Y+5g&pvLKEJcX6R=C+pn{qne7H*B zqh(9`{rJ6pcb9fQDeV?b9#OI6|HPJL zEoFk!kY8fbJY4vZdZ*8t`ZEWa#23OY*6|^W0;*7=FkfitfZ@slg zO|uB1k)lOkurv=d&C&mxH~W9BVy0DK^AvaWxqJ-$6qb7{HwKrlgh_D8J3P!*)6yr! zgyP;w1YjBC;9PT5I=(KT@5{Qcc*;YDYuERG&S|K!MO5W6&#>&$1=huoSlr<(6+{_p zoX~x8R^38jsU3HU2j61{ol+#p+KB}!ilx`WhMK|Jm692DF)NntRFZ^@#s#V;T~=Q2 z`-b=mZR+Vot95j?wMgS2DL+$~=-~(p%)~4Pc^2iYGz*10FooUK;kQMdB&Y=-IcMUDG0n13UA*nfJZ-y*IO`(kTtW_;l;X z-E|qEuf7qDcnJ1p05s7uBKR7*iUrQ0EXpGyjUgg0;l)@)=&$D?mOq3gPaK{PTr9g6&iUy`K$!w(;fSBeRhq(y=ZLFvO@Vm_+bOR}17 zS}wB<%c***!wtqwyZR`7y24q|7Anjd^O6r3UvXSUZN0`CLm<8VWb@m{_q)$G+Rev4 zcVxc@ty>MQTSdu>`2=sksRu3g)QVH9>9*l|$shqUdht^S(iLU67sp^$kPc*7Mu=?o zu!mBX?g|P{J$@!K&DFJF=Pg=*>?xOV&;=RwD&t;qa&q!4HEmOvAWrjC8U&-%)2JV- zVKKl-O7Ayx%NGvT5T&aocZFrz%y#@^gVmv5%&wTs<^GL%!?TuPeBM`lGc0tFFh9T* zlUatRIdv93GA~Q1E1^^(ByZNU5 z`os5Eo8<#FmHY3v4Ao6h-Iqf5Un>opPoeKnC1<7j8ux#tU{V0M8!$aZ9R(#$c4TQ( zY2N5)C^5357Pi$wYdF`+=C+PaZRO9sTi&{Sd+ScEgHRpMO0Z-vN!_@d&^l^B7~fXM zTVwfFcA}LnbO&N^(2MlvpwP;m`1|1aenxKekg|TAAB{9Fz=KE_K|@N$O3?FO0e;td tZh1hSI@7zvi+lo144-=hrjCp;{;43G?<$9}@^o4Xc%+RG zJ%50Pl7>IPZ*U6~M@NgqW9+>n7i@VvGoHscGrpf?38VFS|NG;kkg+dLSX~hb+jPt& zBEWzvR`Hy3c85U#;Sqxn`8aqT<^e?etlycF9qV(ORAr&%v}|fKa;nu8ldw(4JRpKB z=fKFF7d#I!8h2daf1S{A5nIv5PDh_WuHrBr!}l`NIP8dOs}-`E^!^vK1utZ;UQK+a!%`1NL+FJ6*OKeTb90d-jyHl?@+jX_b%3hnt-NlesCOGWu3|JZFNz9TjsH5Fk2V^g0D z!RNBssnnDvsTx4lhK?Hr3$DgzEte^2rh<%G|FcW6e01nFd;QqP!rKaA6X8r}3W-mJ zcrug9;c^n-gzCq7RHD%Vm2A)5R8!QuF8|#H?Dc!->~EK-z$VQE7ZZNZ^aXP97I)GWfTacgp@>7%kWI_5Hw zI1K(oorniK{T)T_rv0nc{Kx(9o_o~R9(aITaNuk8`jZk`?ji-Z@%6T0H(XBR`jUo! E0fBJ!8vp>P{wCAAftgHh(Vb_lhJP_LlF~@{~09t#SuujSjD(x zCFZ5)L#d~|v!1S7^m5_O=d;(v6sH!Ir55ET<|U@57RBUdrWYlaWaj4;$Hd2H=4F<| l$LkeT-r}&y%}*)KNwq6t1)2x4wiv|t#LURZSi}ru0RS~#EhYc} literal 0 HcmV?d00001 diff --git a/src/servermanager/migrations/__pycache__/__init__.cpython-38.pyc b/src/servermanager/migrations/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a76714a744ee0c9cd773760fd4306d1f2acceb0 GIT binary patch literal 155 zcmWIL<>g`kf)l~7GC=fW5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!H6i&czER$^Xy zK9q9G$xqkKO)M@+Es7~lEh= 3: + self.userinfo = WxUserInfo() + self.save_session() + return "超过验证次数" + self.userinfo.Count += 1 + self.save_session() + return "验证失败,请重新输入管理员密码:" + + # 管理员命令执行 + if self.userinfo.isAdmin and self.userinfo.isPasswordSet: + # 确认执行命令 + if self.userinfo.Command != '' and info.upper() == 'Y': + return cmd_handler.run(self.userinfo.Command) + else: + # 获取命令帮助 + if info.upper() == 'HELPME': + return cmd_handler.get_help() + # 保存命令等待确认 + self.userinfo.Command = info + self.save_session() + return "确认执行: " + info + " 命令?" + + # 默认使用ChatGPT回复 + return ChatGPT.chat(info) + + +class WxUserInfo(): + """微信用户信息类,用于存储用户会话状态""" + + def __init__(self): + self.isAdmin = False # 是否为管理员 + self.isPasswordSet = False # 是否通过密码验证 + self.Count = 0 # 密码尝试次数 + self.Command = '' # 待确认的命令 \ No newline at end of file diff --git a/src/servermanager/tests.py b/src/servermanager/tests.py new file mode 100644 index 0000000..2f57a6c --- /dev/null +++ b/src/servermanager/tests.py @@ -0,0 +1,121 @@ +# 导入Django测试相关模块 +from django.test import Client, RequestFactory, TestCase +from django.utils import timezone +# 导入WeRoBot文本消息类 +from werobot.messages.messages import TextMessage + +# 导入应用模型 +from accounts.models import BlogUser +from blog.models import Category, Article +# 导入要测试的API类 +from servermanager.api.commonapi import ChatGPT +from .models import commands +# 导入要测试的机器人处理类和方法 +from .robot import MessageHandler, CommandHandler +from .robot import search, category, recents + + +# 创建服务器管理器测试类 +class ServerManagerTest(TestCase): + """ + 服务器管理器功能测试类 + 测试servermanager应用的各项功能 + """ + + def setUp(self): + """ + 测试初始化方法 + 在每个测试方法执行前运行,用于设置测试环境 + """ + # 创建测试客户端,用于模拟HTTP请求 + self.client = Client() + # 创建请求工厂,用于构建请求对象 + self.factory = RequestFactory() + + def test_chat_gpt(self): + """ + 测试ChatGPT功能 + 验证ChatGPT API是否能正常返回响应 + """ + content = ChatGPT.chat("你好") # 发送测试消息 + self.assertIsNotNone(content) # 断言响应内容不为空 + + def test_validate_comment(self): + """ + 综合测试方法 + 测试博客系统和微信机器人的完整功能链 + """ + # 创建超级用户 + user = BlogUser.objects.create_superuser( + email="liangliangyy1@gmail.com", + username="liangliangyy1", + password="liangliangyy1") + + # 使用创建的超级用户登录 + self.client.login(username='liangliangyy1', password='liangliangyy1') + + # 创建分类 + c = Category() + c.name = "categoryccc" + c.save() + + # 创建文章 + article = Article() + article.title = "nicetitleccc" + article.body = "nicecontentccc" + article.author = user + article.category = c + article.type = 'a' # 文章类型 + article.status = 'p' # 发布状态 + article.save() + + # 测试搜索功能 + s = TextMessage([]) # 创建空的文本消息对象 + s.content = "nice" # 设置消息内容 + rsp = search(s, None) # 调用搜索函数 + # 注意:这里没有对搜索结果进行断言,可能需要补充 + + # 测试分类功能 + rsp = category(None, None) # 调用分类函数 + self.assertIsNotNone(rsp) # 断言响应不为空 + + # 测试最近文章功能 + rsp = recents(None, None) # 调用最近文章函数 + self.assertTrue(rsp != '暂时还没有文章') # 断言有文章返回 + + # 测试命令功能 + # 创建测试命令 + cmd = commands() + cmd.title = "test" + cmd.command = "ls" # Linux列表命令 + cmd.describe = "test" + cmd.save() + + # 测试命令处理器 + cmdhandler = CommandHandler() + rsp = cmdhandler.run('test') # 执行测试命令 + self.assertIsNotNone(rsp) # 断言命令执行结果不为空 + + # 测试消息处理器 + s.source = 'u' # 设置消息来源 + s.content = 'test' # 设置消息内容 + msghandler = MessageHandler(s, {}) # 创建消息处理器 + + # 注释掉的管理员权限设置 + # msghandler.userinfo.isPasswordSet = True + # msghandler.userinfo.isAdmin = True + + # 测试各种消息处理场景 + msghandler.handler() # 处理'test'消息 + s.content = 'y' # 确认执行命令 + msghandler.handler() + s.content = 'idcard:12321233' # 身份证查询(建设中功能) + msghandler.handler() + s.content = 'weather:上海' # 天气查询(建设中功能) + msghandler.handler() + s.content = 'admin' # 进入管理员模式 + msghandler.handler() + s.content = '123' # 输入管理员密码 + msghandler.handler() + s.content = 'exit' # 退出管理员模式 + msghandler.handler() \ No newline at end of file diff --git a/src/servermanager/urls.py b/src/servermanager/urls.py new file mode 100644 index 0000000..f3b73f5 --- /dev/null +++ b/src/servermanager/urls.py @@ -0,0 +1,22 @@ +# 导入Django的URL路由模块 +from django.urls import path +# 导入WeRoBot的Django视图创建工具 +from werobot.contrib.django import make_view + +# 从当前目录的robot模块导入robot实例 +from .robot import robot + +# 定义应用的命名空间,用于URL反向解析 +app_name = "servermanager" + +# 定义URL模式列表 +urlpatterns = [ + # 微信机器人消息接收接口 + # 路径: /servermanager/robot + # 使用WeRoBot的make_view将robot实例转换为Django视图 + path(r'robot', make_view(robot)), + # 说明: + # - r'robot': URL路径模式,匹配"robot"路径 + # - make_view(robot): 将WeRoBot机器人实例包装成Django视图函数 + # - 这个端点用于接收微信服务器发送的消息和事件 +] \ No newline at end of file diff --git a/src/servermanager/views.py b/src/servermanager/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/src/servermanager/views.py @@ -0,0 +1 @@ +# Create your views here.