]> gitweb.pimeys.fr Git - today.git/blob - today_server.py
[serveur] Séparation de la config
[today.git] / today_server.py
1 #!/usr/bin/python
2 # -*- encoding: utf-8 -*-
3
4 """ Codé par 20-100
5 script qui affiche des trucs à penser, des J-n des conneries
6 or that kind of stuff.
7
8 Partie serveur, prévue pour chercher périodiquement les trucs non lus
9 et répondre à un check.
10
11 """
12
13 import re
14 import BeautifulSoup
15 from lxml import etree
16 import os
17 import sys
18 import urllib
19 import json
20 import traceback
21 import inspect
22 import pprint
23 os.chdir('/home/vincent/scripts/today/')
24 sys.path.append("/home/vincent/scripts/dtc/")
25 import dtc
26
27 # Config server
28 import serverconfig
29
30 def last_dtc():
31 """Vérifie si il y a des quotes DTC non lues"""
32 return dtc.last_inserted()
33
34 def last_xkcd():
35 p = urllib.urlopen("http://xkcd.com")
36 t = p.read()
37 current_id = int(re.findall("Permanent link to this comic: http://xkcd.com/(.*?)/", t)[0])
38 return current_id
39
40 def last_xantah():
41 p = urllib.urlopen("http://www.adoprixtoxis.com/lite/download/xantah_downloads.php")
42 t = p.read()
43 ids = re.findall("""<div class="top">Xantah (.*?)</div>""", t)
44 ids = [int(i) for i in ids]
45 return max(ids)
46
47 def last_visiteur():
48 p = urllib.urlopen("http://www.levisiteurdufutur.com/episodes.html")
49 t = p.read()
50 # On parse
51 soup = BeautifulSoup.BeautifulSoup(t)
52 # On récupère les différentes saisons
53 saisons = soup.findAll("div", attrs={"id" : "episodes_list"})
54 nsaisons = len(saisons)
55 # La saison en cours est la première dans la liste
56 episodes = saisons[0].findAll("div", attrs={"class" : "thumbCaption"})
57 nepisodes = len(episodes)
58 return nsaisons * 100 + nepisodes
59
60 def parse_youtube(username):
61 """Récupère les vidéos d'une chaîne Youtube"""
62 link = "https://gdata.youtube.com/feeds/api/users/%s/uploads?start-index=1&max-results=50" % (username,)
63 entries = []
64 while link:
65 p = urllib.urlopen(link)
66 t = p.read()
67 x = etree.fromstring(t)
68 # lxml ne supporte pas les namespaces vides dans les requêtes XPath
69 ns = x.nsmap
70 ns["default"] = ns[None]
71 ns.pop(None)
72 # Il y a potentiellement une suite
73 nextlinks = x.xpath("//default:link[@rel='next']", namespaces=ns)
74 if nextlinks:
75 link = nextlinks[0].attrib["href"]
76 else:
77 link = False
78 localentries = x.xpath("//default:entry", namespaces=ns)
79 entries.extend(localentries)
80 titles = [e.xpath(".//default:title", namespaces=ns)[0].text for e in entries]
81 return titles
82
83 def get_season_episode(title):
84 """Récupère les numéros de la saison et de l'épisode. Crash si ne trouve pas."""
85 ep = int(re.findall("ep([0-9]*)", title)[0])
86 saison = int(re.findall("s([0-9]*)", title)[0])
87 return saison, ep
88
89 def last_noob_warpzone():
90 global last_nw
91 # GRUIK
92 if "last_nw" in globals().keys():
93 return last_nw
94 titles = parse_youtube("Funglisoft")
95 noobs = [t.lower().strip() for t in titles if t.lower().strip().startswith("noob")]
96 warpzones = [t.lower().strip() for t in titles if t.lower().strip().startswith("warpzone project")]
97 lasts = []
98 for serie in [noobs, warpzones]:
99 # Les titres sont dans l'ordre antichronologique, on s'arrête donc au premier qu'on comprend
100 for titre in serie:
101 if "noob le film" in titre or "making of" in titre or "noob versus rct" == titre or "extraits ost" in titre:
102 continue
103 try:
104 if DEBUG:
105 print titre
106 saison, ep = get_season_episode(titre)
107 except (ValueError, IndexError) as e:
108 print "%s sur un season_episode warpzone : %s\n" % (e, titre)
109 continue
110 lasts.append([saison, ep])
111 del saison, ep
112 break
113 last_noob = lasts[0][0]*100 + lasts[0][1]
114 last_warp = lasts[1][0]*100 + lasts[1][1]
115 last_nw = [last_noob, last_warp]
116 return last_nw
117
118 def last_noob():
119 return last_noob_warpzone()[0]
120 def last_warpzone():
121 return last_noob_warpzone()[1]
122
123 def last_hugo():
124 titles = parse_youtube("HugoToutSeul")
125 return len(titles)
126
127 def last_norman():
128 titles = parse_youtube("NormanFaitDesVideos")
129 return len(titles)
130
131 def last_cyprien():
132 titles = parse_youtube("MonsieurDream")
133 return len(titles)
134
135 def last_grenier():
136 titles = parse_youtube("joueurdugrenier")
137 return len(titles)
138
139 def last_jl8():
140 rss = urllib.urlopen("http://limbero.org/jl8/rss/")
141 t = rss.read()
142 x = etree.fromstring(t)
143 links = x.xpath("//link")
144 maxnum = links[1].text.split("/")[-1]
145 maxnum = int(maxnum)
146 return maxnum
147
148 def get_file():
149 """Récupère la liste des derniers ids de chaque truc, stockée dans le fichier."""
150 f = open(serverconfig.store_published_file)
151 news = json.load(f)
152 f.close()
153 return news
154
155 def update_file(news):
156 """Met à jour la liste des derniers ids dans le fichier."""
157 f = open(serverconfig.store_published_file, 'w')
158 json.dump(news, f)
159 f.close()
160
161 FETCHS = {
162 "xkcd" : last_xkcd,
163 "dtc" : last_dtc,
164 "xantah" : last_xantah,
165 "visiteur" : last_visiteur,
166 "noob" : last_noob,
167 "warpzone" : last_warpzone,
168 "hugo" : last_hugo,
169 "norman" : last_norman,
170 "cyprien" : last_cyprien,
171 "grenier" : last_grenier,
172 "dc" : last_jl8,
173 }
174
175 def fetch_all():
176 """Va chercher sur les différents sites les nouveaux trucs."""
177 news = {}
178 for (k, f) in FETCHS.iteritems():
179 try:
180 news[k] = f()
181 except Exception as e:
182 errmsg = "Erreur à la récupération de %s :\n" % k
183 errmsg += traceback.format_exc()
184 # On dumpe le contenu local de la mémoire au moment de l'exception
185 fobj = inspect.trace()[-1][0]
186 # On fait un peu de ménage
187 d = {k:v for (k,v) in fobj.f_locals.iteritems() if not k.startswith("_")}
188 # On évite d'envoyer truckLoadsOfShit
189 d = {k: (v if len(str(v)) < 800
190 else str(v)[:400] + "*" * 40 + "TRUNCATED OBJECT" + "*" * 40 + str(v)[-400:])
191 for (k,v) in d.iteritems()}
192 errmsg += "\nContexte : %s\n\n" % (pprint.pformat(d))
193 print errmsg
194 return news
195
196 def sync():
197 """Reçoit une requête de synchronisation."""
198 # On récupère où en est le client sur stdin
199 t = sys.stdin.read()
200 on_client = json.loads(t)
201 # On récupère où en est le serveur dans le fichier idoine
202 if os.path.isfile(serverconfig.store_seen_file):
203 on_server = json.load(open(serverconfig.store_seen_file))
204 else:
205 on_server = {}
206 # On garde le maximum
207 for k in set(on_client.keys() + on_server.keys()):
208 on_server[k] = max(on_client.get(k, 0), on_server.get(k, 0))
209 # On enregistre ce nouveau dico
210 json.dump(on_server, open(serverconfig.store_seen_file, "w"))
211 # On envoie au client ce nouveau dico
212 print json.dumps(on_server)
213
214 if __name__ == "__main__":
215 DEBUG = ("--debug" in sys.argv) or ("--verbose" in sys.argv) or serverconfig.DEBUG
216 if sys.argv[1] == "check":
217 news = fetch_all()
218 if "--init" in sys.argv:
219 olds = news
220 else:
221 olds = get_file()
222 olds.update(news)
223 update_file(olds)
224 elif sys.argv[1] == "whatsup":
225 news = get_file()
226 print json.dumps(news)
227 elif sys.argv[1] == "sync":
228 sync()