]> gitweb.pimeys.fr Git - bots/basile.git/blob - nk.py
s/canal/auteur/ (can't believe this error stayed so long…)
[bots/basile.git] / nk.py
1 #!/usr/bin/python
2 # -*- coding:utf-8 -*-
3
4 # Codé par 20-100
5
6 """ Module de Basile pour dialoguer avec la NoteKfet2015 """
7
8 import socket
9 import json
10 import ssl
11 import traceback
12
13 #: Config de basile
14 import config
15
16 class NKError(Exception):
17 def __init__(self,msg):
18 Exception.__init__(self)
19 self.msg = msg
20 def __str__(self):
21 return str(self.msg)
22 def __unicode__(self):
23 return unicode(self.msg)
24
25 class NKRefused(NKError):
26 pass
27
28 class NKHelloFailed(NKError):
29 pass
30
31 class NKUnknownError(NKError):
32 pass
33
34 class NKDeadServer(NKError):
35 pass
36
37 def full_read(socket):
38 # On récupère d'abord la taille du message
39 length_str = ''
40 char = socket.recv(1)
41 while char != '\n':
42 length_str += char
43 char = socket.recv(1)
44 total = int(length_str)
45 # On utilise une memoryview pour recevoir les données chunk par chunk efficacement
46 view = memoryview(bytearray(total))
47 next_offset = 0
48 while total - next_offset > 0:
49 recv_size = socket.recv_into(view[next_offset:], total - next_offset)
50 next_offset += recv_size
51 try:
52 msg = json.loads(view.tobytes())
53 except (TypeError, ValueError) as e:
54 raise NKNotJson("L'objet reçu n'est pas un JSON")
55 return msg
56
57 def connect():
58 sock = socket.socket()
59 try:
60 # On établit la connexion sur port 4242
61 sock.connect((config.nk_server, config.nk_port))
62 # On passe en SSL
63 sock = ssl.wrap_socket(sock, ca_certs='../keys/ca_.crt')
64 # On fait un hello
65 sock.write(json.dumps(["hello", "Basile"]))
66 # On récupère la réponse du hello
67 out = full_read(sock)
68 except Exception as exc:
69 # Si on a foiré quelque part, c'est que le serveur est down
70 raise NKRefused(str(exc))
71 if out["retcode"] == 0:
72 return sock
73 elif out["retcode"] == 11:
74 raise NKHelloFailed(out["errmsg"])
75 else:
76 raise NKUnknownError(out["errmsg"])
77
78
79 def login(username, password, typ="bdd"):
80 sock = connect()
81 if typ == "special": # ça c'est pour Basile lui-même
82 masque = []
83 elif typ == "bdd":
84 masque = [[], [], True]
85 try:
86 # Basile a un compte special user
87 commande = ["login", [username, password, typ, masque]]
88 sock.write(json.dumps(commande))
89 out = full_read(sock)
90 except Exception as exc:
91 # Si on a foiré quelque part, c'est que le serveur est down
92 raise NKRefused(str(exc))
93 return out, sock
94
95 def get_infos(sock, idbde, serv, canal):
96 """Récupère les données de l'utilisateur NK n°``idbde``"""
97 try:
98 sock.write(json.dumps(["compte", idbde]))
99 ret = full_read(sock)
100 retcode = ret["retcode"]
101 if retcode == 0:
102 return ret["msg"]
103 else:
104 serv.privmsg(canal, ret["errmsg"].encode("utf-8"))
105 except Exception as exc:
106 trace = traceback.format_exc()
107 msg = "failed\n%s" % trace
108 for l in msg.split("\n"):
109 serv.privmsg(canal, l)
110 #log(self.serveur, "priv", auteur, " ".join(message) + "[failed]")
111
112 def get_solde(sock, idbde, serv, canal):
113 """Récupère le (success, solde, pseudo) de l'utilisateur NK n°``idbde``"""
114 infos = get_infos(sock, idbde, serv, canal)
115 if infos:
116 return (True, infos["solde"], infos["pseudo"])
117 else:
118 return (False, None, None)
119
120 def consomme(sock, idbde, conso, serv, canal):
121 """Fais consommer une conso à l'utilisateur NK n°``idbde``"""
122 try:
123 sock.write(json.dumps(["get_boutons", ["", ""]]))
124 ret = full_read(sock)
125 retcode = ret["retcode"]
126 if retcode == 0:
127 boutons = ret["msg"]
128 boutons = [b for b in boutons if b["label"].lower() == conso.lower()]
129 if len(boutons) == 0:
130 serv.privmsg(canal, (u"Impossible de trouver la conso %s" % (conso)).encode("utf-8"))
131 return False
132 bouton = boutons[0]
133 sock.write(json.dumps(["consos", [[bouton["id"], idbde, 1]]]))
134 ret = full_read(sock)
135 if ret["retcode"] == 0:
136 [[retcode, [idbouton, idbde], errmsg]] = ret["msg"]
137 if retcode != 0:
138 serv.privmsg(canal, errmsg.encode("utf-8"))
139 else:
140 success, solde, pseudo = get_solde(sock, idbde, serv, canal)
141 if success:
142 serv.privmsg(canal, (u"%s consomme 1 %s (nouveau solde : %.2f)" % (pseudo, bouton["label"], solde/100.0)).encode("utf-8"))
143 else:
144 serv.privmsg(canal, (u"%s consommé mais impossible de récupérer le solde après transaction." % (bouton["label"])).encode("utf-8"))
145 return True # on a réussi à consommer la conso
146 else:
147 serv.privmsg(canal, ret["errmsg"].encode("utf-8"))
148 return False
149 else:
150 serv.privmsg(canal, ret["errmsg"])
151 except Exception as exc:
152 trace = traceback.format_exc()
153 msg = "failed\n%s" % trace
154 for l in msg.split("\n"):
155 serv.privmsg(canal, l)
156