From 8f6fb7e1613ae26e59c2477735bc72e2a381202e Mon Sep 17 00:00:00 2001 From: qo-op Date: Mon, 30 Nov 2020 16:03:47 +0100 Subject: [PATCH] Add cesium-messaging code --- zen/cesium-messaging/.env | 3 + zen/cesium-messaging/.env.template | 4 + zen/cesium-messaging/README.md | 62 ++++ .../__pycache__/natools.cpython-36.pyc | Bin 0 -> 9288 bytes .../__pycache__/userEnv.cpython-36.pyc | Bin 0 -> 234 bytes zen/cesium-messaging/dialog.py | 76 ++++ zen/cesium-messaging/gchange-profile.py | 55 +++ .../cesiumMessaging.cpython-36.pyc | Bin 0 -> 5022 bytes .../lib/__pycache__/gchange.cpython-36.pyc | Bin 0 -> 6236 bytes .../lib/__pycache__/natools.cpython-36.pyc | Bin 0 -> 9292 bytes zen/cesium-messaging/lib/cesiumMessaging.py | 298 ++++++++++++++++ zen/cesium-messaging/lib/gchange.py | 324 ++++++++++++++++++ zen/cesium-messaging/lib/natools.py | 297 ++++++++++++++++ zen/cesium-messaging/requirements.txt | 6 + zen/cesium-messaging/setup.sh | 12 + zen/cesium-messaging/userEnv.py | 3 + 16 files changed, 1140 insertions(+) create mode 100644 zen/cesium-messaging/.env create mode 100644 zen/cesium-messaging/.env.template create mode 100644 zen/cesium-messaging/README.md create mode 100644 zen/cesium-messaging/__pycache__/natools.cpython-36.pyc create mode 100644 zen/cesium-messaging/__pycache__/userEnv.cpython-36.pyc create mode 100755 zen/cesium-messaging/dialog.py create mode 100755 zen/cesium-messaging/gchange-profile.py create mode 100644 zen/cesium-messaging/lib/__pycache__/cesiumMessaging.cpython-36.pyc create mode 100644 zen/cesium-messaging/lib/__pycache__/gchange.cpython-36.pyc create mode 100644 zen/cesium-messaging/lib/__pycache__/natools.cpython-36.pyc create mode 100755 zen/cesium-messaging/lib/cesiumMessaging.py create mode 100644 zen/cesium-messaging/lib/gchange.py create mode 100755 zen/cesium-messaging/lib/natools.py create mode 100644 zen/cesium-messaging/requirements.txt create mode 100755 zen/cesium-messaging/setup.sh create mode 100644 zen/cesium-messaging/userEnv.py diff --git a/zen/cesium-messaging/.env b/zen/cesium-messaging/.env new file mode 100644 index 0000000..ba01fab --- /dev/null +++ b/zen/cesium-messaging/.env @@ -0,0 +1,3 @@ +DUNIKEY²="/home/fred/.ssb/trousseau-2L8vaYix-Fred-gchange-PubSec.dunikey" # Chemin du fichier de trousseau Ḡ1 de l'émetteur, au format PubSec +#POD="https://g1.data.duniter.fr" # Noeud Cecium+ utilisé pour l'envoi du message +POD="https://data.gchange.fr" # Noeud Gchange utilisé pour l'envoi du message diff --git a/zen/cesium-messaging/.env.template b/zen/cesium-messaging/.env.template new file mode 100644 index 0000000..5063635 --- /dev/null +++ b/zen/cesium-messaging/.env.template @@ -0,0 +1,4 @@ +DUNIKEY="" # Chemin de la clé privé Ḡ1 de l'émetteur, au format PubSec +#POD="https://g1.data.duniter.fr" # Adresse du pod Cesium ou Gchange à utiliser +POD="https://g1.data.le-sou.org" # Adresse du pod Cesium de secours +#POD="https://data.gchange.fr" # Adresse du pod ḠChange à utiliser \ No newline at end of file diff --git a/zen/cesium-messaging/README.md b/zen/cesium-messaging/README.md new file mode 100644 index 0000000..7c82476 --- /dev/null +++ b/zen/cesium-messaging/README.md @@ -0,0 +1,62 @@ +# Utilisation de la messagerie Cesium+/Gchange +## Réception/Envoi/Suppression de messages + +## Installation + +Linux: +``` +bash setup.sh +``` + +Autre: +``` +Débrouillez-vous. +``` + +## Utilisation + +Renseignez le fichier **.env** (Généré lors de la première tentative d'execution, ou à copier depuis .env.template). + +### Lecture des messages +``` +./dialog.py read +``` + +_Options_: +``` +-h, --help show this help message and exit +-n NUMBER, --number NUMBER + Affiche les NUMBER derniers messages +-o, --outbox Lit les messages envoyés +``` + +### Envoi de messages +``` +./dialog.py send -d DESTINATAIRE +``` + +_Options_: +``` +-h, --help show this help message and exit +-d DESTINATAIRE, --destinataire DESTINATAIRE + Destinataire du message +-t TITRE, --titre TITRE + Titre du message à envoyer +-m MESSAGE, --message MESSAGE + Message à envoyer +-f FICHIER, --fichier FICHIER + Envoyer le message contenu dans le fichier 'FICHIER' +-o, --outbox Envoi le message sur la boite d'envoi +``` + +### Suppression de messages +``` +./dialog.py delete -i ID +``` + +_Options_: +``` +-h, --help show this help message and exit +-i ID, --id ID ID du message à supprimer +-o, --outbox Suppression d'un message envoyé +``` diff --git a/zen/cesium-messaging/__pycache__/natools.cpython-36.pyc b/zen/cesium-messaging/__pycache__/natools.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bad3c5ee8362a5d84c2f86ec236ae9eaae82598f GIT binary patch literal 9288 zcmbVS%WoVvoMh-#ZY=R&U2iVgh3*-_Y*i%jk5abUC_Rt4kauSe3faI|7CHZ~TJ^y&szy7Jl-%#+9f&~RHD|ki02?Zy=$l5RQ zDOI0V@T!8>6r53TR>8S1GWG>N&lj-5%i3T?2qyHd-Ww6ae38G^HS96emiYHjyNKEi{(aP5MeXgbZjYl}R%5TB_71;^vDZ;s zQMEV3_~(Yqi1T8+tAA|RC5Q#&1O{@w6%nB3rvmSY4gJTu*_^-vVd#2+k!% zi}81<_jB`Ll{n|=a~eMZ84dXnt>$0Fy!VdG8xb#w*UxF8UFa?0UD;M?;+WzuZCz3OEm4!d;CEu~Qt~tL zI=`#NJ`*GS_i9(6?{msCjoP)Y7V+htO?XZGHXiw~Cv|Py9`B?3WkUDMZ$)=A;rt%B zOUqSop7zflyINWY6>PYwbn85o;qY zcM&y>mnd48wYl0n--JG9Y4@Nfjv`#`;0M}6jho!GXMZ^Rk>(m7p|znE?AE^+nr7v1 zdd-8|q8zZ9t20;GUB@pu4R*`j{?}hMs#`(^Y$<5%?>JTW?Y0O449jXWnDXTI4a>@J zR;oU0N^e^_I}Fvb6oUC)IoNZgSY!uYiaDBlXtWQ0DMR2wS zVnkz=*w~2KV5=UeJ3-ZRCzz;WT(VsueYDwZZ=smn1oNbozvKkOx@2CHW);Bi0c$wm ztT%D$7}*a+uQ3=tf|1t;}36m|#9wU8)2@ z(_fsP-rL)o+ICyydDCzJGQK~3W6El$uT9NPU3*MP$$F?g)=?JHq3Ou&Fv~^RX*GgS zcf%Aq{4gU$(2{N`MdFWg}G#kPV(^WY0ejz1^xI|v0$qa!kz^^npfa0sG(-m(=Ok=2H;8i!wY_({IhAAl=9;V>(ZfJOZm})vfCA7qT6Wbk5FQK12 z_MuZ$1NxTIL<9!GX_zMhMZ$OmP2U30^qij32aQ2PzJ%8CV&1?=M^|EAB(VZ8UQ2Z~ zjzplACZP~k&juNcWox+(B{F>@N<4F24ueW)>}?hD@-$U5Wlo~Vld1txu_4h}g@wH^ zy(g;(2QOkwo*_V1fqW_AJ{*w3hu0}NpA^zkGnJHYU`}kwoPOSrucCcKLNcau%u!S# z;}Q2YlVplqE(MjgT;c^TV%Eh$n2!SF`{KYJ6mCf#G=riqoJPTrV;~b|5H5lDBwyJW zcM>?h@+MtgLj8D#$Tl6l2Jh-IqT7R9JiS`BDXY83Ef*tS?OX0L>Z>K%FZ4%CB%b`o zX!H~S|J5I*(Mr__W;*Fwmd?FLWMI9P3-UFqgAA}X&;@sovI=t$aHx~54J*u?>O0XP zpU>4sy2hh?$CPd-UpqAHztSiG7EaeZf z02-29F-*IjTN2@*gJZr_DMqaf@hPO7C5#--O~sLXi<;(^xBsLc4WO?W`x4FZX?Y{V7Xe~0>(OVp&`6;b>?)fD>MRHI`e0xCEvy~dk{Ci>LduUGjF+Q0K| zRU*bAA!6z1dTM-y)`?>QsU)FJ_9A>0oIXb7U@Ed&fWV88OF=3_yDvIZ9vMNdhF!_g zuWKD+1IKPKP`BiVI`UUti$}3%Q6I#~gYsdNsnW@sT5k{2u5xg43wG!IF{^#z9fvdW zOw~7LN#dw5)Mq_qpKfgR{gMwRc=?DHFouBfn;7HY`!K8oV;Hv}O@0;Q{1-owP#H*Y zMu4MB7U7^a%1+QL?1F2(FpFFu8YJ<+(Y~e zH>f%dUnP1?2w}gdko$(B8H1`j7D1&&F(w!Hg{0DCB!?{D=pK>Xu^ zQ-o|`8Vxre-?NXJVPWw}&0qBHuD_KEQTH{FhoOdgTwtab|J znJAt}Y(GuKjQ^n;l6OhR5aOZQ=NaU9+d<_i38B;m84pT20D7Fe4eKMi+(7#=1)&lF z`lB*~pW5$qme8)G)KvZGTvMqjVkPz-{iBG<5gqBpB}(qH>VQ$FK2B7e`YOKJqj3gN zar{cruQ)UF3u=9J5`~#4K|tUlS>!zcJ6k$lq7#uY#ZpC->V9kICqV86{0FF)ly*=d zVY2usVg3jej%yLFNk8pqHPmrX>w%8o0M?ORn{~~f>1(HObz0!r4Q+oVF!0Em;kmB4 zqWx$N2LlYmGv^@oA%2(oKnVt2>5U=~YGs7BPgY>myh_H>)Gk=Km@Fz&%1!Vk$@Ny1 zeUYQBKK?)aQKgW=bp?0mKp8qr;}*RwB*_>n3SRHXzz_B6lV%@O+KKJ{GtBTQ9VgGW zbOt{i1w4gP{yusWjgjW)r!+=!pHfI%5*2m$+Zei@Tq(EIaCWx1bK_4i=;No!BEa!9 z>HH_{ci~oap;H%7m4}3x$XkwIMUnhdOjZjVp-o4W;+Jx7a@QyjM^{^wgD%X$8)s*p zN&oZbrXS@}_c8Aq*#5KUC+lDecwU%$CeJ_rmc&oCWW#sLqWHTk^5@@jk^I*6`DaS- z7d_&IY-mbnFEp+#gvRVlXyEQ(EX>*F!dxpfuFoH_`g3}z@}Ixzt#pLaH~LR{>mU#~ z-RSheG4u^q{_E!^E@Lat&cwm**!`tA|C{G0o@Dvo!1LceKiR}HpULwV-HsDDlE1{%6ovwI1jLz|vF?l@-rdIwmRPi!2YAVJXW8LH z7F#NLJ3Dw1y1{-Jy|4N|vaFj?pTB4^20y1)3#R^V#SMB`!5G9j99xMS@fdENlgZrv zdF;})04arI@iNinAs$j!^n>0|bezURD^(mg&H+77 zBVoY8&3ZzQvEC?d^f3lKt4cf;yWFzwsE22zjmc6nt2ZZJrgv(C^kkVWm3M+0K)oAf zatv$6*(kddKZ7nZN+wAMYLptZHnLoLYULG%q!$meW%^Z!YLzX~TOG)*xlO!BQ&!2S zr;p?$(VJXL){ik&9=ym9h1j(PHao*0=fWJjKHoPLgKBa;S%#Qbj2|v^nWSJy(Y-?7 z)XQq3%a+tD#SOMjPZ#VGLhuBenZt(w#2Vn!)PCDft|!V|R<9g*Xr_sQ$MC#IA|9F2 zb4B_HKvGV+Ue#6hSn=qI8t?q2MvDu!<8L#kOdkl6ODW-diC}jk>R+$GJBb2&s9&Q& zgz)3F*knui#-dz6c^tH%UX6%z#e79Hno9hQinqt8rMltI>RSj?-Em8eaIw|erWq@$ zg3mQjF+OuBx5hV&2E)bKlYYUn*1Z7VdoE61WIMufeWsp<>5D}4a5`lncHTy)=u3Ij zg9C++Fb~h)D-uJ$K+~seNz)A`KS3GhibeYRR4leLlaqwg&QDIpT(*tLvit$CP^m`u zKVz~k*P@;#dOiH^sJMp8T6=oU-E|sO<@V%fy)87x?luIp6?HJbDH;u2*{0g(j!syF zr+AD$T#U8fd*DdA*DbPRNlq~OzQaWmUr2DX;>l(VWXnYlX0kRuZ25)u>3DH`NZYMK z$I<>CYtx%gFQS2c7r*tWGmc;FZ0wRM*T*nE&XMftA3s|B@gwz^L@3IRZ7aUA{`e7$ zv8j?`e6!G1#*CW}CC(~^E-ZxEVv&2LVlf;j7GqTxi|Sciy_wiU$(!>Ov>4rWCuqJp zD(PU8?*piBF1z?x5XjKPl`>4>J6)i@Sg3oedhu3|k?Ms^y$h;mtmpxbQc-mUjBYoS ztg9S5G!g3XzexmA6bM}WswLQ;;lPPKCcJ|LhjLl;rAhoYSxUt{gTDuuRQYZw=$ z;-S&hZ6SK$Q?GR%k@_BiF(MT`j^dd&z7qcdI>Y>}{gP1eJJf|-A!J5C`THSIo}XL7 zrfN0B4Z2eJUjn3zlyTi)87rU5qaXeoO!d{x+^d!`NTYx` zK)OZKjarpP1`kphv~sws5JcTb;pQ6kT{1@WDg2_Ag_35?J<&6RHvWPBS>tpvMsqWG c+`V9&$4FFC&mjJtHZ0RJMp4V*o|Mu5ADnWNOnSj>#P%`=w$mUOMcgu@r9}j8 z!#OT4k&1_58igynjUl1J7TE!=x9;8;g=5Cjhzf3aRT$88rHd%3?5p$h z<%aj?*6XX5^*3wg=b(QbE&m9>EzY`Dv$(P*Gxv7WHdm+Vn5)}#&DCpqxY}Jm4Vpn3 zHp8^kET!dUnbCTuJD1Ki=S<)2R?_+AJhR@mxX1mEE$&b3<^t{l9^x*<-6764tXkbcGAaf^$N9QIX>>5#XTP;n~I4?0;llY)0h z#`B+z&ULi>B0yMlCY*3iIz z-hHcQe_$ye>%57b1dtJqZD5I8*n!{B)~LEZ=*7@L6z7ph`)W9P<#t!Ja}jOD-EO3I zMAUDqok(TTFdN8-6b9WywB*d~!RChO)XGm8cB%dTU^Ay%9q>Y^F5hi;2jWdBGpU_? zm}@^*Tu71AIyW(-Ir2Bj|S)wFGC+^3hr?k`0c+G*iM0ZP8Fi9Q*{Lzw2xIDX7 z-^o%@--2oCk23in@3%Xm-Z8p7mx?@ZZ^ymudN9wh}95#UG#lg9o!LG_)V*`2cD<3W?e!Kh!Q4c}E&Odn#64L4Mg& zq$aPI^*TZ?NVrprT)V9X5-T>dtgC&PQ3(ktLZ&qj%FHApU<+>>!b8?#+cQ9yW! zho}D7nb>1*>~jCNPGU`vAXcqif9ylsVMEp+ReNy{CmU666iPj(aK=%!-S2nfPFuxU zub$*tZ?yd6!fw5AE>VB-@@~DAi?;0S)NCE>K>tGKFjZT%wYw9mycTGe)@hFq(ta+# zPp=B4fTnYWnf)wRFi|GcwldCL3yj+38B8=AlixE-c-9%DFt$8Lx4snlpsNnrppuIo zhXu^c%b1ui0QSsaAF)ICG#i~bpz_%@Q>~Lv`)Ee%zk}B)OR>ZxI3yBhVkIuL=&V}M zxC8HYS1qI!k9$Zqu);D@@iHYIEAh--ux#zRDp29rO-f_Lz{KVyvKO;{&mNzJgz~Dj zj{b+IcD=F3=Zw5EdX>UbeBO-AjeWj=kw8_F`Eh`^2WlZXgwaE^7wn;`4&$v=bp&{n zS7CQ6nZrysIX0m^R`=!~-kZN@<}ZHX{6)wmG_A zy!LowRLYeaMka!plw>gNsZ6!IqopWnWa*|95f{;(ujW^yXRJQ5BV1n6{!nzg*&|t} zRZc7B@;m5B6md(QA~;DfNAL}TyeL{1 z(I`Cg?)uf}$!_f#JBI{>-2ap+*(kWRnFw_?s-4hIjFe?(x%T>5U-Yy~X-j)K0!_&u zVloPaLV-Y`fxA=qYrg}}M-KC1j#Ol}sr^mVs7qILY4g$*)a{ImV(9Wk3|+oh3>D{~ z^kHg@sUh0mdqyWiT%wq5Nb+?(Pf3xIZLZpBUx(Pn7Trp!@wIZ14}&z{esAMe!$c@H zpFPTTDei4$cS!ezMt#iLkR78+lMRF}70H3NPsM8{aaQR3MQIZGTX>eA1z7e;yUb3p zC3b|BQIv{SWf7xR#^@=#%8ug>W$Iu^&q|zzG?gj`&4e`B7fuC6L1_~qOlcH>Jh6#c z!NFLgX3HO7miz#)R}5ZcB^^*!`|N4tI%aR8<@7oWb#!#}K)#{PHEJOh;9ffwt(Gpg zT4}}yUFug_t%rklw|FASu|Tq22$;v?8}%N*>YGenA;3fLmEvS&P-r4lqiz~_m~!<`hL}O1WnAZB$KmQD{YdK`}4C z;7p6Nn)y#Lreg0zmrSmbC#XkcsmK5(iJ8RqO&W^`zC}PZrRK?(sQ+z(H3E|ize|sp z5&RkVH%0lNQmD3ng!YeepQU|@`mZq`N$KWm@E*fRhHS%>De1m|>Yf+=#q#h9(NtHmp zO+a);zC-XXf!W-9)cYyH&j@Z382+`Nw@CWiGn>34Ts6!%^h>G+@3UHmG-p zz^LUH)H8YTF7@sa?5889-2VyVU!8^QkHLRG2a)$bXZ!0I`zvjq%9`OHhxet1J@z4+ zcq)HM@GF9^L^M9cikuqxCx}L6k7&?%ty;K&skA2ZnQUlmOy+F{_&UdTc`k{CNQZ#7 uDvhzprp*$4shj^f74Aa3Mw09qQ%zZq4zCs5=`|8%c65QQfOl1#h2-zGbv_LM literal 0 HcmV?d00001 diff --git a/zen/cesium-messaging/lib/__pycache__/gchange.cpython-36.pyc b/zen/cesium-messaging/lib/__pycache__/gchange.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7156da5db8de52ff047b5a41fd40618a412ef06a GIT binary patch literal 6236 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 literal 0 HcmV?d00001 diff --git a/zen/cesium-messaging/lib/cesiumMessaging.py b/zen/cesium-messaging/lib/cesiumMessaging.py new file mode 100755 index 0000000..e27e370 --- /dev/null +++ b/zen/cesium-messaging/lib/cesiumMessaging.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 + +import os, sys, ast, requests, json, base58, base64, time, string, random, re +from lib.natools import fmt, sign, get_privkey, box_decrypt, box_encrypt +from hashlib import sha256 +from datetime import datetime +from termcolor import colored + +VERSION = "0.1.1" +PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" + +def pp_json(json_thing, sort=True, indents=4): + # Print beautifull JSON + if type(json_thing) is str: + print(json.dumps(json.loads(json_thing), sort_keys=sort, indent=indents)) + else: + print(json.dumps(json_thing, sort_keys=sort, indent=indents)) + return None + +class ReadFromCesium: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.recipient = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.recipient) or len(self.recipient) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Configure JSON document to send + def configDoc(self, nbrMsg, outbox): + boxType = "issuer" if outbox else "recipient" + + data = {} + data['sort'] = { "time": "desc" } + data['from'] = 0 + data['size'] = nbrMsg + data['_source'] = ['issuer','recipient','title','content','time','nonce','read_signature'] + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = {} + data['query']['bool']['filter']['term'] = {} + data['query']['bool']['filter']['term'][boxType] = self.recipient + + document = json.dumps(data) + return document + + def sendDocument(self, nbrMsg, outbox): + boxType = "outbox" if outbox else "inbox" + + document = self.configDoc(nbrMsg, outbox) + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/message/{1}/_search'.format(self.pod, boxType), headers=headers, data=document) + if result.status_code == 200: + return result.json()["hits"] + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text) + + # Parse JSON result and display messages + def readMessages(self, msgJSON, nbrMsg, outbox): + def decrypt(msg): + msg64 = base64.b64decode(msg) + return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode() + + # Get terminal size + rows = int(os.popen('stty size', 'r').read().split()[1]) + + totalMsg = msgJSON["total"] + if nbrMsg > totalMsg: + nbrMsg = totalMsg + + if totalMsg == 0: + print(colored("Aucun message à afficher.", 'yellow')) + return True + else: + infoTotal = " Nombre de messages: " + str(nbrMsg) + "/" + str(totalMsg) + " " + print(colored(infoTotal.center(rows, '#'), "yellow")) + for hits in msgJSON["hits"]: + self.idMsg = hits["_id"] + msgSrc = hits["_source"] + self.issuer = msgSrc["issuer"] + nonce = msgSrc["nonce"] + nonce = base58.b58decode(nonce) + self.dateS = msgSrc["time"] + date = datetime.fromtimestamp(self.dateS).strftime(", le %d/%m/%Y à %H:%M ") + if outbox: + startHeader = " À " + msgSrc["recipient"] + else: + startHeader = " De " + self.issuer + headerMsg = startHeader + date + "(ID: {})".format(self.idMsg) + " " + + print('-'.center(rows, '-')) + print(colored(headerMsg, "blue").center(rows+9, '-')) + print('-'.center(rows, '-')) + try: + self.title = decrypt(msgSrc["title"]) + self.content = decrypt(msgSrc["content"]) + except Exception as e: + sys.stderr.write(colored(str(e), 'red') + '\n') + pp_json(hits) + continue + print("Objet: " + self.title) + print(self.content) + # pp_json(hits) + + + def read(self, nbrMsg, outbox): + jsonMsg = self.sendDocument(nbrMsg, outbox) + self.readMessages(jsonMsg, nbrMsg, outbox) + + + + +#################### Sending class #################### + + + + +class SendToCesium: + def __init__(self, dunikey, pod, recipient, outbox): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + self.recipient = recipient + self.outbox = outbox + + # Generate pseudo-random nonce + nonce=[] + for i in range(32): + nonce.append(random.choice(string.ascii_letters + string.digits)) + self.nonce = base64.b64decode(''.join(nonce)) + + if not re.match(PUBKEY_REGEX, recipient) or len(recipient) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + + def encryptMsg(self, msg): + return fmt["64"](box_encrypt(msg.encode(), get_privkey(self.dunikey, "pubsec"), self.recipient, self.nonce)).decode() + + def configDoc(self, title, msg): + b58nonce = base58.b58encode(self.nonce).decode() + + # Get current timestamp + timeSent = int(time.time()) + + # Generate custom JSON + data = {} + data['issuer'] = self.issuer + data['recipient'] = self.recipient + data['title'] = title + data['content'] = msg + data['time'] = timeSent + data['nonce'] = b58nonce + data['version'] = 2 + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + finalDoc = '{' + '"hash":"{0}","signature":"{1}",'.format(hashDoc, signature) + document[1:] + + return finalDoc + + + def sendDocument(self, document): + boxType = "outbox" if self.outbox else "inbox" + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get result + try: + result = requests.post('{0}/message/{1}?pubkey={2}'.format(self.pod, boxType, self.recipient), headers=headers, data=document) + except Exception as e: + sys.stderr.write("Impossible d'envoyer le message:\n" + str(e)) + sys.exit(1) + else: + if result.status_code == 200: + print(colored("Message envoyé avec succès !", "green")) + print("ID: " + result.text) + return result + else: + sys.stderr.write("Erreur inconnue:" + '\n') + print(str(pp_json(result.text)) + '\n') + + def send(self, title, msg): + finalDoc = self.configDoc(self.encryptMsg(title), self.encryptMsg(msg)) # Configure JSON document to send + self.sendDocument(finalDoc) # Send final signed document + + + + +#################### Deleting class #################### + + + + +class DeleteFromCesium: + def __init__(self, dunikey, pod, outbox): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + self.outbox = outbox + + + def configDoc(self, idMsg): + # Get current timestamp + timeSent = int(time.time()) + + boxType = "outbox" if self.outbox else "inbox" + + # Generate document to customize + data = {} + data['version'] = 2 + data['index'] = "message" + data['type'] = boxType + data['id'] = idMsg + data['issuer'] = self.issuer + data['time'] = timeSent + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, idMsg): + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get result + try: + result = requests.post('{0}/history/delete'.format(self.pod), headers=headers, data=document) + if result.status_code == 404: + raise ValueError("Message introuvable") + elif result.status_code == 403: + raise ValueError("Vous n'êtes pas l'auteur de ce message.") + except Exception as e: + sys.stderr.write(colored("Impossible de supprimer le message {0}:\n".format(idMsg), 'red') + str(e) + "\n") + return False + else: + if result.status_code == 200: + print(colored("Message {0} supprimé avec succès !".format(idMsg), "green")) + return result + else: + sys.stderr.write("Erreur inconnue.") + + def delete(self, idsMsgList): + for idMsg in idsMsgList: + finalDoc = self.configDoc(idMsg) + self.sendDocument(finalDoc, idMsg) + diff --git a/zen/cesium-messaging/lib/gchange.py b/zen/cesium-messaging/lib/gchange.py new file mode 100644 index 0000000..2292e89 --- /dev/null +++ b/zen/cesium-messaging/lib/gchange.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 + +import os, sys, ast, requests, json, base58, base64, time, string, random, re +from lib.natools import fmt, sign, get_privkey, box_decrypt, box_encrypt +from time import sleep +from hashlib import sha256 +from datetime import datetime +from termcolor import colored + +PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" + +class ReadLikes: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Configure JSON document to send + def configDoc(self, profile): + if not profile: profile = self.issuer + # elif len(profile) < 42: + # print(len(profile)) + # gProfile = requests.get('{0}/user/profile/{1}'.format(self.pod, issuer)) + # gProfile = json.loads(gProfile.text)['_source'] + # pseudo = gProfile['title'] + + data = {} + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = [ + {'term': {'index': 'user'}}, + {'term': {'type': 'profile'}}, + {'term': {'id': profile}}, + {'term': {'kind': 'STAR'}} + ] + # data['query']['bool']['should'] = {'term':{'issuer': self.issuer}} + data['size'] = 5000 + data['_source'] = ['issuer','level'] + data['aggs'] = { + 'level_sum': { + 'sum': { + 'field': 'level' + } + } + } + + return json.dumps(data) + + def sendDocument(self, document): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/like/record/_search'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + # print(result.text) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text + '\n') + + def parseResult(self, result): + result = json.loads(result) + totalLikes = result['hits']['total'] + totalValue = result['aggregations']['level_sum']['value'] + if totalLikes: + score = totalValue/totalLikes + else: + score = 0 + raw = result['hits']['hits'] + finalPrint = {} + finalPrint['likes'] = [] + for i in raw: + issuer = i['_source']['issuer'] + gProfile = self.getProfile(issuer) + pseudo = gProfile['title'] + payTo = gProfile['pubkey'] + id = i['_id'] + level = i['_source']['level'] + if issuer == self.issuer: + finalPrint['yours'] = { 'id' : id, 'pseudo' : pseudo, 'level' : level } + else: + finalPrint['likes'].append({ 'issuer' : issuer, 'pseudo' : pseudo, 'payTo' : payTo, 'level' : level }) + finalPrint['score'] = score + + return json.dumps(finalPrint) + + def getProfile(self, profile): + headers = { + 'Content-type': 'application/json', + } + + data = {} + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = [ + {'term': {'_index': 'user'}}, + {'term': {'_type': 'profile'}}, + {'term': {'_id': profile}} + ] + data['_source'] = ['title','pubkey'] + + data = json.dumps(data) + + result = requests.post('{0}/user/profile/_search'.format(self.pod), headers=headers, data=data) + result = json.loads(result.text)['hits']['hits'][0]['_source'] + + return result + + def readLikes(self, profile=False): + document = self.configDoc(profile) + result = self.sendDocument(document) + result = self.parseResult(result) + + print(result) + return result + + + + +#################### Like class #################### + + + + +class SendLikes: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Configure JSON document to send + def configDoc(self, profile, likes): + if not profile: profile = self.issuer + if likes not in range(0, 6): + sys.stderr.write(colored('Votre like doit être compris entre 0 et 5.\n', 'red')) + return False + + + timeSent = int(time.time()) + + data = {} + data['version'] = 2 + data['index'] = "user" + data['type'] = "profile" + data['id'] = profile + data['kind'] = "STAR" + data['level'] = likes + data['time'] = timeSent + data['issuer'] = self.issuer + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, pubkey): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/user/profile/:id/_like'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + print(colored("Profile liké avec succès !", 'green')) + return result.text + elif result.status_code == 400: + resultJson = json.loads(result.text) + if 'DuplicatedDocumentException' in resultJson['error']: + rmLike = UnLikes(self.dunikey, self.pod) + rmLike.unLike(pubkey, True) + sleep(0.5) + self.sendDocument(document, pubkey) + return resultJson['error'] + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + resultJson['error'] + '\n') + else: + resultJson = json.loads(result.text) + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + resultJson['error'] + '\n') + + + + + def like(self, stars, profile=False): + document = self.configDoc(profile, stars) + if document: + self.sendDocument(document, profile) + + + + +#################### Unlike class #################### + + + + +class UnLikes: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Check if you liked this profile + def checkLike(self, pubkey): + + readProfileLikes = ReadLikes(self.dunikey, self.pod) + document = readProfileLikes.configDoc(pubkey) + result = readProfileLikes.sendDocument(document) + result = readProfileLikes.parseResult(result) + result = json.loads(result) + + if 'yours' in result: + myLike = result['yours']['id'] + return myLike + else: + sys.stderr.write("Vous n'avez pas liké ce profile\n") + return False + + # Configure JSON document to send + def configDoc(self, idLike): + timeSent = int(time.time()) + + data = {} + data['version'] = 2 + data['index'] = "like" + data['type'] = "record" + data['id'] = idLike + data['issuer'] = self.issuer + data['time'] = timeSent + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, silent): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/history/delete'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + if not silent: + print(colored("Like supprimé avec succès !", 'green')) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text + '\n') + + + def unLike(self, pubkey, silent=False): + idLike = self.checkLike(pubkey) + if idLike: + document = self.configDoc(idLike) + self.sendDocument(document, silent) + diff --git a/zen/cesium-messaging/lib/natools.py b/zen/cesium-messaging/lib/natools.py new file mode 100755 index 0000000..18f06d1 --- /dev/null +++ b/zen/cesium-messaging/lib/natools.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 + +""" + CopyLeft 2020 Pascal Engélibert + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +""" + +__version__ = "1.3.1" + +import os, sys, duniterpy.key, libnacl, base58, base64, getpass + +def getargv(arg:str, default:str="", n:int=1, args:list=sys.argv) -> str: + if arg in args and len(args) > args.index(arg)+n: + return args[args.index(arg)+n] + else: + return default + +def read_data(data_path, b=True): + if data_path == "-": + if b: + return sys.stdin.buffer.read() + else: + return sys.stdin.read() + else: + return open(os.path.expanduser(data_path), "rb" if b else "r").read() + +def write_data(data, result_path): + if result_path == "-": + os.fdopen(sys.stdout.fileno(), 'wb').write(data) + else: + open(os.path.expanduser(result_path), "wb").write(data) + +def encrypt(data, pubkey): + return duniterpy.key.PublicKey(pubkey).encrypt_seal(data) + +def decrypt(data, privkey): + return privkey.decrypt_seal(data) + +def box_encrypt(data, privkey, pubkey, nonce=None, attach_nonce=False): + signer = libnacl.sign.Signer(privkey.seed) + sk = libnacl.public.SecretKey(libnacl.crypto_sign_ed25519_sk_to_curve25519(signer.sk)) + verifier = libnacl.sign.Verifier(base58.b58decode(pubkey).hex()) + pk = libnacl.public.PublicKey(libnacl.crypto_sign_ed25519_pk_to_curve25519(verifier.vk)) + box = libnacl.public.Box(sk.sk, pk.pk) + data = box.encrypt(data, nonce) if nonce else box.encrypt(data) + return data if attach_nonce else data[24:] + +def box_decrypt(data, privkey, pubkey, nonce=None): + signer = libnacl.sign.Signer(privkey.seed) + sk = libnacl.public.SecretKey(libnacl.crypto_sign_ed25519_sk_to_curve25519(signer.sk)) + verifier = libnacl.sign.Verifier(base58.b58decode(pubkey).hex()) + pk = libnacl.public.PublicKey(libnacl.crypto_sign_ed25519_pk_to_curve25519(verifier.vk)) + box = libnacl.public.Box(sk.sk, pk.pk) + return box.decrypt(data, nonce) if nonce else box.decrypt(data) + +def sign(data, privkey): + return privkey.sign(data) + +def verify(data, pubkey): + try: + ret = libnacl.sign.Verifier(duniterpy.key.PublicKey(pubkey).hex_pk()).verify(data) + sys.stderr.write("Signature OK!\n") + return ret + except ValueError: + sys.stderr.write("Bad signature!\n") + exit(1) + +def get_privkey(privkey_path, privkey_format): + if privkey_format == "pubsec": + if privkey_path == "*": + privkey_path = "privkey.pubsec" + return duniterpy.key.SigningKey.from_pubsec_file(privkey_path) + + elif privkey_format == "cred": + if privkey_path == "*": + privkey_path = "-" + if privkey_path == "-": + return duniterpy.key.SigningKey.from_credentials(getpass.getpass("Password: "), getpass.getpass("Salt: ")) + else: + return duniterpy.key.SigningKey.from_credentials_file(privkey_path) + + elif privkey_format == "seedh": + if privkey_path == "*": + privkey_path = "authfile.seedhex" + return duniterpy.key.SigningKey.from_seedhex(read_data(privkey_path, False)) + + elif privkey_format == "wif": + if privkey_path == "*": + privkey_path = "authfile.wif" + return duniterpy.key.SigningKey.from_wif_or_ewif_file(privkey_path) + + elif privkey_format == "wifh": + if privkey_path == "*": + privkey_path = "authfile.wif" + return duniterpy.key.SigningKey.from_wif_or_ewif_hex(privkey_path) + + elif privkey_format == "ssb": + if privkey_path == "*": + privkey_path = "secret" + return duniterpy.key.SigningKey.from_ssb_file(privkey_path) + + elif privkey_format == "key": + if privkey_path == "*": + privkey_path = "authfile.key" + return duniterpy.key.SigningKey.from_private_key(privkey_path) + + print("Error: unknown privkey format") + +def fill_pubkey(pubkey, length=32): + while pubkey[0] == 0: + pubkey = pubkey[1:] + return b"\x00"*(length-len(pubkey)) + pubkey + +def pubkey_checksum(pubkey, length=32, clength=3): + return base58.b58encode(libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(fill_pubkey(base58.b58decode(pubkey), length)))).decode()[:clength] + +# returns (pubkey:bytes|None, deprecated_length:bool) +def check_pubkey(pubkey): + if ":" in pubkey: + parts = pubkey.split(":") + if len(parts[1]) < 3 or len(parts[1]) > 32: + return (None, False) + for i in range(32, 0, -1): + if pubkey_checksum(parts[0], i, len(parts[1])) == parts[1]: + return (parts[0], i < 32) + return (None, False) + return (pubkey, False) + +fmt = { + "raw": lambda data: data, + "16": lambda data: data.hex().encode(), + "32": lambda data: base64.b32encode(data), + "58": lambda data: base58.b58encode(data), + "64": lambda data: base64.b64encode(data), + "64u": lambda data: base64.urlsafe_b64encode(data), + "85": lambda data: base64.b85encode(data), +} + +defmt = { + "raw": lambda data: data, + "16": lambda data: bytes.fromhex(data), + "32": lambda data: base64.b32decode(data), + "58": lambda data: base58.b58decode(data), + "64": lambda data: base64.b64decode(data), + "85": lambda data: base64.b85decode(data), +} + +def show_help(): + print("""Usage: +python3 natools.py [options] + +Commands: + encrypt Encrypt data + decrypt Decrypt data + box-encrypt Encrypt data (NaCl box) + box-decrypt Decrypt data (NaCl box) + sign Sign data + verify Verify data + pubkey Display pubkey + pk Display b58 pubkey shorthand + +Options: + -c Display pubkey checksum + -f Private key format (default: cred) + key cred pubsec seedh ssb wif wifh + -i Input file path (default: -) + -I Input format: raw 16 32 58 64 85 (default: raw) + -k Privkey file path (* for auto) (default: *) + -n Nonce (b64, 24 bytes) (for NaCl box) + -N Attach nonce to output (for NaCl box encryption) + --noinc Do not include msg after signature + -o Output file path (default: -) + -O Output format: raw 16 32 58 64 64u 85 (default: raw) + -p Pubkey (base58) + + --help Show help + --version Show version + --debug Debug mode (display full errors) + +Note: "-" means stdin or stdout. +""") + +if __name__ == "__main__": + + if "--help" in sys.argv: + show_help() + exit() + + if "--version" in sys.argv: + print(__version__) + exit() + + privkey_format = getargv("-f", "cred") + data_path = getargv("-i", "-") + privkey_path = getargv("-k", "*") + pubkey = getargv("-p") + result_path = getargv("-o", "-") + output_format = getargv("-O", "raw") + input_format = getargv("-I", "raw") + + if pubkey: + pubkey, len_deprecated = check_pubkey(pubkey) + if not pubkey: + print("Invalid pubkey checksum! Please check spelling.") + exit(1) + if len(base58.b58decode(pubkey)) > 32: + print("Invalid pubkey: too long!") + exit(1) + if len_deprecated: + print("Warning: valid pubkey checksum, but deprecated format (truncating zeros)") + + try: + if sys.argv[1] == "encrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + write_data(fmt[output_format](encrypt(defmt[input_format](read_data(data_path)), pubkey)), result_path) + + elif sys.argv[1] == "decrypt": + write_data(fmt[output_format](decrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format))), result_path) + + elif sys.argv[1] == "box-encrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + nonce = getargv("-n", None) + if nonce: + nonce = base64.b64decode(nonce) + attach_nonce = "-N" in sys.argv + write_data(fmt[output_format](box_encrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format), pubkey, nonce, attach_nonce)), result_path) + + elif sys.argv[1] == "box-decrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + nonce = getargv("-n", None) + if nonce: + nonce = base64.b64decode(nonce) + write_data(fmt[output_format](box_decrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format), pubkey, nonce)), result_path) + + elif sys.argv[1] == "sign": + data = defmt[input_format](read_data(data_path)) + signed = sign(data, get_privkey(privkey_path, privkey_format)) + + if "--noinc" in sys.argv: + signed = signed[:len(signed)-len(data)] + + write_data(fmt[output_format](signed), result_path) + + elif sys.argv[1] == "verify": + if not pubkey: + print("Please provide pubkey!") + exit(1) + write_data(fmt[output_format](verify(defmt[input_format](read_data(data_path)), pubkey)), result_path) + + elif sys.argv[1] == "pubkey": + if pubkey: + if "-c" in sys.argv and output_format == "58": + write_data("{}:{}".format(pubkey, pubkey_checksum(pubkey)).encode(), result_path) + else: + write_data(fmt[output_format](base58.b58decode(pubkey)), result_path) + else: + pubkey = get_privkey(privkey_path, privkey_format).pubkey + if "-c" in sys.argv and output_format == "58": + write_data("{}:{}".format(pubkey, pubkey_checksum(pubkey)).encode(), result_path) + else: + write_data(fmt[output_format](base58.b58decode(pubkey)), result_path) + + elif sys.argv[1] == "pk": + if not pubkey: + pubkey = get_privkey(privkey_path, privkey_format).pubkey + if "-c" in sys.argv: + print("{}:{}".format(pubkey, pubkey_checksum(pubkey))) + else: + print(pubkey) + + else: + show_help() + + except Exception as e: + if "--debug" in sys.argv: + 0/0 # DEBUG MODE (raise error when handling error to display backtrace) + sys.stderr.write("Error: {}\n".format(e)) + show_help() + exit(1) diff --git a/zen/cesium-messaging/requirements.txt b/zen/cesium-messaging/requirements.txt new file mode 100644 index 0000000..bf25335 --- /dev/null +++ b/zen/cesium-messaging/requirements.txt @@ -0,0 +1,6 @@ +wheel +base58 +pybase64 +duniterpy +termcolor +python-dotenv diff --git a/zen/cesium-messaging/setup.sh b/zen/cesium-messaging/setup.sh new file mode 100755 index 0000000..ac3f027 --- /dev/null +++ b/zen/cesium-messaging/setup.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +for i in gcc python3-pip python3-setuptools libpq-dev python3-dev python3-wheel; do + if [ $(dpkg-query -W -f='${Status}' $i 2>/dev/null | grep -c "ok installed") -eq 0 ]; then + [[ ! $j ]] && sudo apt update + sudo apt install -y $i + j=1 + fi +done + +pip3 install -r requirements.txt +chmod u+x dialog.py diff --git a/zen/cesium-messaging/userEnv.py b/zen/cesium-messaging/userEnv.py new file mode 100644 index 0000000..91dde5f --- /dev/null +++ b/zen/cesium-messaging/userEnv.py @@ -0,0 +1,3 @@ +DUNIKEY²="/home/fred/.ssb/trousseau-2L8vaYix-Fred-gchange-PubSec.dunikey" # Chemin du fichier de trousseau Ḡ1 de l'émetteur, au format PubSec +POD="https://g1.data.duniter.fr" # Noeud Cecium+ utilisé pour l'envoi du message +#pod="https://data.gchange.fr" # Noeud Gchange utilisé pour l'envoi du message