caesium.py 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956
  1. #!/usr/bin/env python3
  2. import curses, os, urllib.request, urllib.parse, base64, codecs, pickle, time, subprocess, re, hashlib, webbrowser, locale
  3. from datetime import datetime
  4. from shutil import copyfile
  5. from keys import *
  6. lasts = {}
  7. color_theme = "default"
  8. bold = [False, False, False, False, False, False, False, False, False, False, False]
  9. counts = []
  10. counts_rescan = True
  11. echo_counts = {}
  12. next_echoarea = False
  13. depth = 50
  14. fdepth = 5
  15. messages = []
  16. version = "Caesium/0.4 RC1 │"
  17. splash = [ "▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀",
  18. "████████ ████████ ████████ ████████ ███ ███ ███ ██████████",
  19. "███ ███ ███ ███ ███ ███ ███ ███ ██ ███",
  20. "███ ████████ ████████ ████████ ███ ███ ███ ███ ██ ███",
  21. "███ ███ ███ ███ ███ ███ ███ ███ ███ ██ ███",
  22. "████████ ████████ ████████ ████████ ███ ████████ ███ ██ ███",
  23. "▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄",
  24. " ncurses ii/idec client v.0.4 RC1",
  25. " Andrew Lobanov 04.07.2017"]
  26. urltemplate=re.compile("((https?|ftp|file)://?[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|])")
  27. def reset_config():
  28. global nodes, node, editor, oldquote, db
  29. nodes = []
  30. node = 0
  31. editor = ""
  32. oldquote = False
  33. db = 2
  34. def check_directories():
  35. if not os.path.exists("out"):
  36. os.mkdir("out")
  37. if not os.path.exists("fecho"):
  38. os.mkdir("fecho")
  39. for n in nodes:
  40. if not os.path.exists("out/" + n["nodename"]):
  41. os.mkdir("out/" + n["nodename"])
  42. if db == 0:
  43. if not os.path.exists("echo"):
  44. os.mkdir("echo")
  45. if not os.path.exists("msg"):
  46. os.mkdir("msg")
  47. if not os.path.exists("echo/favorites"):
  48. open("echo/favorites", "w")
  49. if not os.path.exists("echo/carbonarea"):
  50. open("echo/carbonarea", "w")
  51. elif db == 1:
  52. if not os.path.exists("aio"):
  53. os.mkdir("aio")
  54. elif db == 2:
  55. if not os.path.exists("ait"):
  56. os.mkdir("ait")
  57. def check_config():
  58. if not os.path.exists("caesium.cfg"):
  59. default_config = open("caesium.def.cfg", "r").read()
  60. open("caesium.cfg","w").write(default_config)
  61. #
  62. # Взаимодействие с нодой
  63. #
  64. def separate(l, step=20):
  65. for x in range(0, len(l), step):
  66. yield l[x:x+step]
  67. def load_config():
  68. global nodes, editor, color_theme, show_splash, oldquote, db, browser, depth, fdepth
  69. nodes = []
  70. first = True
  71. node = {}
  72. echoareas = []
  73. fechoareas = []
  74. archive = []
  75. browser = webbrowser
  76. config = open("caesium.cfg").read().split("\n")
  77. for line in config:
  78. param = line.split(" ")
  79. if param[0] == "nodename":
  80. if not first:
  81. node["echoareas"] = echoareas
  82. node["fechoareas"] = fechoareas
  83. node["archive"] = archive
  84. node["clone"] = []
  85. if not "to" in node:
  86. node["to"] = []
  87. nodes.append(node)
  88. else:
  89. first = False
  90. node = {}
  91. echoareas = []
  92. fechoareas = []
  93. archive = []
  94. node["nodename"] = " ".join(param[1:])
  95. elif param[0] == "node":
  96. node["node"] = param[1]
  97. if not node["node"].endswith("/"):
  98. node["node"] = node["node"] + "/"
  99. elif param[0] == "auth":
  100. node["auth"] = param[1]
  101. elif param[0] == "echo":
  102. if len(param) == 2:
  103. echoareas.append([param[1], "", False])
  104. else:
  105. echoareas.append([param[1], " ".join(param[2:]), False])
  106. elif param[0] == "stat":
  107. if len(param) == 2:
  108. echoareas.append([param[1], "", True])
  109. else:
  110. echoareas.append([param[1], " ".join(param[2:]), True])
  111. elif param[0] == "fecho":
  112. fechoareas.append(param[1])
  113. elif param[0] == "to":
  114. node["to"] = " ".join(param[1:]).split(",")
  115. elif param[0] == "archive":
  116. if len(param) == 2:
  117. archive.append([param[1], "", True])
  118. else:
  119. archive.append([param[1], " ".join(param[2:]), True])
  120. elif param[0] == "editor":
  121. editor = " ".join(param[1:])
  122. elif param[0] == "theme":
  123. color_theme = param[1]
  124. elif param[0] == "nosplash":
  125. show_splash = False
  126. elif param[0] == "oldquote":
  127. oldquote = True
  128. elif param[0] == "depth":
  129. try:
  130. depth = int(param[1])
  131. except:
  132. None
  133. elif param[0] == "fdepth":
  134. try:
  135. fdepth = int(param[1])
  136. except:
  137. None
  138. elif param[0] == "db":
  139. if param[1] == "txt":
  140. db = 0
  141. elif param[1] == "aio":
  142. db = 1
  143. elif param[1] == "ait":
  144. db = 2
  145. elif param[1] == "sqlite":
  146. db = 3
  147. elif param[0] == "browser":
  148. browser = webbrowser.GenericBrowser(param[1])
  149. if not "nodename" in node:
  150. node["nodename"] = "untitled node"
  151. if not "to" in node:
  152. node["to"] = []
  153. node["echoareas"] = echoareas
  154. node["fechoareas"] = fechoareas
  155. node["archive"] = archive
  156. node["clone"] = []
  157. nodes.append(node)
  158. for i in range(0, len(nodes)):
  159. nodes[i]["echoareas"].insert(0, ["favorites", "Избранные сообщения", True])
  160. nodes[i]["echoareas"].insert(1, ["carbonarea", "Карбонка", True])
  161. def load_colors():
  162. global bold
  163. colors = ["black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "gray"]
  164. params = ["border", "titles", "cursor", "text", "quote1", "quote2", "comment", "url", "header", "statusline", "scrollbar"]
  165. try:
  166. theme = open("themes/" + color_theme + ".cfg", "r").read().split("\n")
  167. except:
  168. theme = open("themes/default.cfg", "r").read().split("\n")
  169. for line in theme:
  170. param = line.split(" ")
  171. if len(param) > 1:
  172. if param[1] == "grey":
  173. param[1] = "gray"
  174. if param[0] in params:
  175. fg = colors.index(param[1])
  176. if param[2] == "default":
  177. bg = -1
  178. else:
  179. bg = colors.index(param[2])
  180. if param[0] == "border":
  181. curses.init_pair(1, fg, bg)
  182. if len(param) == 4:
  183. bold[0] = True
  184. else:
  185. bold[0] = False
  186. if param[0] == "titles":
  187. curses.init_pair(2, fg, bg)
  188. if len(param) == 4:
  189. bold[1] = True
  190. else:
  191. bold[1] = False
  192. if param[0] == "cursor":
  193. curses.init_pair(3, fg, bg)
  194. if len(param) == 4:
  195. bold[2] = True
  196. else:
  197. bold[2] = False
  198. if param[0] == "text":
  199. curses.init_pair(4, fg, bg)
  200. if len(param) == 4:
  201. bold[3] = True
  202. else:
  203. bold[3] = False
  204. if param[0] == "quote1":
  205. curses.init_pair(5, fg, bg)
  206. if len(param) == 4:
  207. bold[4] = True
  208. else:
  209. bold[4] = False
  210. if param[0] == "quote2":
  211. curses.init_pair(6, fg, bg)
  212. if len(param) == 4:
  213. bold[5] = True
  214. else:
  215. bold[5] = False
  216. if param[0] == "comment":
  217. curses.init_pair(7, fg, bg)
  218. if len(param) == 4:
  219. bold[6] = True
  220. else:
  221. bold[6] = False
  222. if param[0] == "url":
  223. curses.init_pair(8, fg, bg)
  224. if len(param) == 4:
  225. bold[7] = True
  226. else:
  227. bold[7] = False
  228. if param[0] == "statusline":
  229. curses.init_pair(9, fg, bg)
  230. if len(param) == 4:
  231. bold[8] = True
  232. else:
  233. bold[8] = False
  234. if param[0] == "header":
  235. curses.init_pair(10, fg, bg)
  236. if len(param) == 4:
  237. bold[9] = True
  238. else:
  239. bold[9] = False
  240. if param[0] == "scrollbar":
  241. curses.init_pair(11, fg, bg)
  242. if len(param) == 4:
  243. bold[10] = True
  244. else:
  245. bold[10] = False
  246. def save_out(draft = False):
  247. new = codecs.open("temp", "r", "utf-8").read().strip().replace("\r", "").split("\n")
  248. if len(new) <= 1:
  249. os.remove("temp")
  250. else:
  251. header = new.index("")
  252. if header == 3:
  253. buf = new
  254. elif header == 4:
  255. buf = new[1:5] + ["@repto:%s" % new[0]] + new[5:]
  256. if draft:
  257. codecs.open(outcount() + ".draft", "w", "utf-8").write("\n".join(buf))
  258. else:
  259. codecs.open(outcount() + ".out", "w", "utf-8").write("\n".join(buf))
  260. os.remove("temp")
  261. def resave_out(filename, draft = False):
  262. new = codecs.open("temp", "r", "utf-8").read().strip().split("\n")
  263. if len(new) <= 1:
  264. os.remove("temp")
  265. else:
  266. if draft:
  267. codecs.open("out/" + nodes[node]["nodename"] + "/" + filename.replace(".out", ".draft"), "w", "utf-8").write("\n".join(new))
  268. else:
  269. codecs.open("out/" + nodes[node]["nodename"] + "/" + filename, "w", "utf-8").write("\n".join(new))
  270. os.remove("temp")
  271. def outcount():
  272. outpath = "out/" + nodes[node]["nodename"]
  273. i = str(len([x for x in os.listdir(outpath) if not x.endswith(".toss")]) + 1)
  274. return outpath + "/%s" % i.zfill(5)
  275. def get_out_length(drafts = False):
  276. try:
  277. if drafts:
  278. return len([f for f in sorted(os.listdir("out/" + nodes[node]["nodename"])) if f.endswith(".draft")]) - 1
  279. else:
  280. return len([f for f in sorted(os.listdir("out/" + nodes[node]["nodename"])) if f.endswith(".out") or f.endswith(".outmsg")]) - 1
  281. except:
  282. return 0
  283. def make_toss():
  284. lst = [x for x in os.listdir("out/" + nodes[node]["nodename"]) if x.endswith(".out")]
  285. for msg in lst:
  286. text = codecs.open("out/" + nodes[node]["nodename"] + "/%s" % msg, "r", "utf-8").read()
  287. coded_text = base64.b64encode(text.encode("utf-8"))
  288. codecs.open("out/" + nodes[node]["nodename"] + "/%s.toss" % msg, "w", "utf-8").write(coded_text.decode("utf-8"))
  289. os.rename("out/" + nodes[node]["nodename"] + "/%s" % msg, "out/" + nodes[node]["nodename"] + "/%s%s" % (msg, "msg"))
  290. def send_mail():
  291. lst = [x for x in sorted(os.listdir("out/" + nodes[node]["nodename"])) if x.endswith(".toss")]
  292. max = len(lst)
  293. n = 1
  294. try:
  295. for msg in lst:
  296. print("\rОтправка сообщения: " + str(n) + "/" + str(max), end="")
  297. text = codecs.open("out/" + nodes[node]["nodename"] + "/%s" % msg, "r", "utf-8").read()
  298. data = urllib.parse.urlencode({"tmsg": text,"pauth": nodes[node]["auth"]}).encode("utf-8")
  299. request = urllib.request.Request(nodes[node]["node"] + "u/point")
  300. result = urllib.request.urlopen(request, data).read().decode("utf-8")
  301. if result.startswith("msg ok"):
  302. os.remove("out/" + nodes[node]["nodename"] + "/%s" % msg)
  303. n = n + 1
  304. elif result == "msg big!":
  305. print("\nERROR: very big message (limit 64K)!")
  306. elif result == "auth error!":
  307. print("\nERROR: unknown auth!")
  308. else:
  309. print("\nERROR: unknown error!")
  310. if len(lst) > 0:
  311. print()
  312. except:
  313. print("\nОшибка: не удаётся связаться с нодой.")
  314. def separate(l, step=40):
  315. for x in range(0, len(l), step):
  316. yield l[x:x+step]
  317. def get_features():
  318. try:
  319. r = urllib.request.Request(nodes[node]["node"] + "x/features")
  320. with urllib.request.urlopen(r) as f:
  321. features = f.read().decode("utf-8").split("\n")
  322. except:
  323. features = []
  324. return features
  325. def check_features(features):
  326. ue = "u/e" in features
  327. xc = "x/c" in features
  328. f = "f/" in features
  329. return ue, xc, f
  330. def load_counts():
  331. counts = {}
  332. if os.path.exists("counts.lst"):
  333. f = open("counts.lst", "rb")
  334. counts = pickle.load(f)
  335. f.close()
  336. else:
  337. counts[node] = {}
  338. if not node in counts:
  339. counts[node] = {}
  340. return counts[node]
  341. def save_counts(counts, remote_counts):
  342. counts[node] = remote_counts
  343. f = open("counts.lst", "wb")
  344. pickle.dump(counts, f)
  345. f.close()
  346. def get_remote_counts():
  347. counts = {}
  348. echoareas = []
  349. for echoarea in nodes[node]["echoareas"]:
  350. if echoarea[0] not in ["favorites", "carbonarea"]:
  351. echoareas.append(echoarea[0])
  352. r = urllib.request.Request(nodes[node]["node"] + "x/c/" + "/".join(echoareas))
  353. with urllib.request.urlopen(r) as f:
  354. c = f.read().decode("utf-8").split("\n")
  355. for count in c:
  356. echoarea = count.split(":")
  357. if len(echoarea) > 1:
  358. counts[echoarea[0]] = int(echoarea[1])
  359. return counts
  360. def calculate_offset(depth):
  361. n = False
  362. offset = 0
  363. echoareas = []
  364. for echoarea in nodes[node]["echoareas"]:
  365. if not echoarea in ["favorites", "carbonarea"]:
  366. echoareas.append(echoarea[0])
  367. for echoarea in echoareas:
  368. if not echoarea in counts[node]:
  369. n = True
  370. else:
  371. if not echoarea in clone and int(remote_counts[echoarea]) - int(counts[node][echoarea]) > offset:
  372. offset = int(remote_counts[echoarea]) - int(counts[node][echoarea])
  373. if not n:
  374. depth = offset
  375. return depth
  376. def get_msg_list(clone, ue, depth):
  377. msg_list = []
  378. fetch_echoareas = []
  379. echoareas = []
  380. for echoarea in nodes[node]["echoareas"]:
  381. if not echoarea[0] in ["favorites", "carbonarea"]:
  382. echoareas.append(echoarea[0])
  383. if ue:
  384. for echoarea in echoareas:
  385. if not echoarea in clone and (not echoarea in counts[node] or int(counts[node][echoarea]) < int(remote_counts[echoarea])):
  386. fetch_echoareas.append(echoarea)
  387. else:
  388. clone = echoareas
  389. if len(clone) > 0:
  390. r = urllib.request.Request(nodes[node]["node"] + "u/e/" + "/".join(clone))
  391. with urllib.request.urlopen(r) as f:
  392. lines = f.read().decode("utf-8").split("\n")
  393. for line in lines:
  394. if len(line) > 0:
  395. msg_list.append(line)
  396. if len(fetch_echoareas) > 0 and int(depth) > 0:
  397. r = urllib.request.Request(nodes[node]["node"] + "u/e/" + "/".join(fetch_echoareas) + "/-%s:%s" %(depth, depth))
  398. with urllib.request.urlopen(r) as f:
  399. lines = f.read().decode("utf-8").split("\n")
  400. for line in lines:
  401. if len(line) > 0:
  402. msg_list.append(line)
  403. return msg_list
  404. def get_bundle(node, msgids):
  405. bundle = []
  406. try:
  407. r = urllib.request.Request(node + "u/m/" + msgids)
  408. with urllib.request.urlopen(r) as f:
  409. bundle = f.read().decode("utf-8").split("\n")
  410. except:
  411. None
  412. return bundle
  413. def debundle(bundle):
  414. global messages
  415. for msg in bundle:
  416. if msg:
  417. m = msg.split(":")
  418. msgid = m[0]
  419. if len(msgid) == 20 and m[1]:
  420. msgbody = base64.b64decode(m[1].encode("ascii")).decode("utf8").split("\n")
  421. messages.append([msgid, msgbody])
  422. if len(messages) >= 1000:
  423. save_message(messages, nodes[node]["node"], nodes[node]["to"])
  424. messages = []
  425. def echo_filter(ea):
  426. rr = re.compile(r'^[a-z0-9_!.-]{1,60}\.[a-z0-9_!.-]{1,60}$')
  427. if rr.match(ea): return True
  428. def get_mail(clone, ue, depth):
  429. fetch_msg_list = []
  430. print("Получение индекса от ноды...")
  431. remote_msg_list = get_msg_list(clone, ue, depth)
  432. print("Построение разностного индекса...")
  433. for line in remote_msg_list:
  434. if echo_filter(line):
  435. if line in clone and ue:
  436. remove_echoarea(line)
  437. local_index = get_echo_msgids(line)
  438. else:
  439. if not line in local_index:
  440. fetch_msg_list.append(line)
  441. msg_list_len = str(len(fetch_msg_list))
  442. if len(fetch_msg_list) > 0:
  443. count = 0
  444. for get_list in separate(fetch_msg_list):
  445. count = count + len(get_list)
  446. print("\rПолучение сообщений: " + str(count) + "/" + msg_list_len, end="")
  447. debundle(get_bundle(nodes[node]["node"], "/".join(get_list)))
  448. save_message(messages, node, nodes[node]["to"])
  449. else:
  450. print("Новых сообщений не обнаружено.", end="")
  451. print()
  452. def get_local_fecho(fecho):
  453. index = []
  454. try:
  455. for f in open("fecho/%s.txt" % fecho, "r").read().split("\n"):
  456. if len(f) > 0:
  457. index.append(f)
  458. except:
  459. None
  460. return index
  461. def get_remote_fecho():
  462. index = []
  463. try:
  464. r = urllib.request.Request(nodes[node]["node"] + "f/e/" + "/".join(nodes[node]["fechoareas"]) + "/-" + str(fdepth) + ":" + str(fdepth))
  465. with urllib.request.urlopen(r) as f:
  466. for row in f.read().decode("utf8").split("\n"):
  467. if len(row) > 0: # and not row in nodes[node]["fechoareas"]:
  468. index.append(row)
  469. except:
  470. None
  471. return index
  472. def download_file(fi):
  473. r = urllib.request.Request("%sf/f/%s/%s" % (nodes[node]["node"], fi[0], fi[1].split(":")[0]))
  474. out = urllib.request.urlopen(r)
  475. file_size=0
  476. block_size=8192
  477. if not os.path.exists("fecho/%s" % fi[0]):
  478. os.mkdir("fecho/%s" % fi[0])
  479. f = open("fecho/%s/%s" % (fi[0], fi[1].split(":")[1]), "wb")
  480. while True:
  481. buffer = out.read(block_size)
  482. if not buffer:
  483. break
  484. file_size += len(buffer)
  485. f.write(buffer)
  486. f.close()
  487. codecs.open("fecho/%s.txt" % fi[0], "a", "utf8").write(fi[1] + "\n")
  488. def get_fecho():
  489. print("Получение индекса файлэх.")
  490. remote_index = get_remote_fecho()
  491. print("Построение разностного индекса.")
  492. local_index = []
  493. index = []
  494. for fecho in nodes[node]["fechoareas"]:
  495. local_fecho = get_local_fecho(fecho)
  496. for fid in local_fecho:
  497. local_index.append(fid)
  498. for fi in remote_index:
  499. if not ":" in fi:
  500. fecho = fi
  501. elif not fi in local_index:
  502. index.append([fecho, fi])
  503. if len(index) == 0:
  504. print("Новых файлов не обнаружено.")
  505. return False
  506. else:
  507. ret = []
  508. for fi in index:
  509. row = fi[1].split(":")
  510. print("Получение файла %s" % row[1], end=" ")
  511. try:
  512. download_file(fi)
  513. print("OK")
  514. ret.append([fi[0], row[1], row[2], row[4]])
  515. except:
  516. print("ERROR")
  517. return ret
  518. def mailer(clone):
  519. global depth, messages
  520. messages = []
  521. print("Работа с " + nodes[node]["node"])
  522. if "auth" in nodes[node]:
  523. make_toss()
  524. send_mail()
  525. print("Получение списка возможностей ноды...")
  526. features = get_features()
  527. ue, xc, f = check_features(features)
  528. if xc:
  529. counts = load_counts()
  530. print("Получение количества сообщений в конференциях...")
  531. remote_counts = get_remote_counts()
  532. depth = calculate_offset(depth)
  533. try:
  534. get_mail(clone, ue, depth)
  535. except:
  536. print("ОШИБКА")
  537. try:
  538. if f:
  539. fecho = get_fecho()
  540. if fecho:
  541. s = []
  542. fe = ""
  543. for f in fecho:
  544. if fe != f[0]:
  545. if not fe == "":
  546. s.append("----\n")
  547. fe = f[0]
  548. s.append("== Файлэха %s:" % fe)
  549. if int(f[2]) < 1024:
  550. size = str(f[2]) + " B"
  551. elif int(f[2]) >= 1024 and int(f[2]) < 1048576:
  552. size = str(int(int(f[2]) / 1024 * 10) / 10) + " KB"
  553. else:
  554. size = str(int(int(f[2]) / 1048576 * 10) / 10) + " MB"
  555. s.append("Файл: " + f[1])
  556. s.append("Размер: " + size)
  557. s.append("Описание: " + f[3])
  558. s.append("")
  559. codecs.open("fecho/%s/%s.txt" % (fe, ".".join(f[1].split(".")[:-1])), "w", "utf-8").write(f[3])
  560. save_to_carbonarea("fetcher", "Новые файлы", "\n".join(s))
  561. if xc:
  562. save_counts(counts, remote_counts)
  563. except:
  564. print("ОШИБКА")
  565. input("Нажмите Enter для продолжения.")
  566. #
  567. # Пользовательский интерфейс
  568. #
  569. echo_cursor = 0
  570. archive_cursor = 0
  571. width = 0
  572. height = 0
  573. show_splash = True
  574. def splash_screen():
  575. stdscr.clear()
  576. x = int((width - len(splash[1])) / 2) - 1
  577. y = int((height - len(splash)) / 2)
  578. i = 0
  579. for line in splash:
  580. stdscr.addstr(y + i, x, line, curses.color_pair(4))
  581. i = i + 1
  582. stdscr.refresh()
  583. curses.napms(2000)
  584. stdscr.clear()
  585. def get_term_size():
  586. global width, height
  587. height, width = stdscr.getmaxyx()
  588. def draw_title(y, x, title):
  589. if bold[0]:
  590. color = curses.color_pair(1) + curses.A_BOLD
  591. else:
  592. color = curses.color_pair(1)
  593. stdscr.addstr(y, x, "[", color)
  594. stdscr.addstr(y, x + 1 + len(title), "]", color)
  595. if bold[1]:
  596. color = curses.color_pair(2) + curses.A_BOLD
  597. else:
  598. color = curses.color_pair(2)
  599. stdscr.addstr(y, x + 1, title, curses.color_pair(2) + curses.A_BOLD)
  600. def draw_status(x, title):
  601. if bold[8]:
  602. color = curses.color_pair(9) + curses.A_BOLD
  603. else:
  604. color = curses.color_pair(9)
  605. stdscr.addstr(height - 1, x, title, color)
  606. def draw_cursor(y, color):
  607. for i in range (0, width):
  608. stdscr.insstr(y + 1, i, " ", color)
  609. def current_time():
  610. draw_status(width - 8, "│ " + datetime.now().strftime("%H:%M"))
  611. def get_counts(new = False, favorites = False):
  612. global echo_counts
  613. for echoarea in nodes[node]["echoareas"]:
  614. if not new:
  615. if not echoarea[0] in echo_counts:
  616. echo_counts[echoarea[0]] = get_echo_length(echoarea[0])
  617. else:
  618. echo_counts[echoarea[0]] = get_echo_length(echoarea[0])
  619. for echoarea in nodes[node]["archive"]:
  620. if not echoarea[0] in echo_counts:
  621. echo_counts[echoarea[0]] = get_echo_length(echoarea[0])
  622. echo_counts["carbonarea"] = len(get_carbonarea())
  623. echo_counts["favorites"] = len(get_favorites_list())
  624. def rescan_counts(echoareas):
  625. counts = []
  626. for echo in echoareas:
  627. try:
  628. echocount = echo_counts[echo[0]]
  629. if echo[0] in lasts:
  630. last = echocount - lasts[echo[0]]
  631. if echocount == 0 and lasts[echo[0]] == 0:
  632. last = 1
  633. else:
  634. last = echocount + 1
  635. except:
  636. echocount = 0
  637. last = 1
  638. if last - 1 < 0:
  639. last = 1
  640. counts.append([str(echocount), str(last - 1)])
  641. return counts
  642. def draw_echo_selector(start, cursor, archive):
  643. global counts, counts_rescan
  644. dsc_lens = []
  645. hidedsc = False
  646. m = 0
  647. if bold[0]:
  648. stdscr.attron(curses.color_pair(1))
  649. stdscr.attron(curses.A_BOLD)
  650. else:
  651. stdscr.attron(curses.color_pair(1))
  652. for i in range(0, width):
  653. if bold[0]:
  654. color = curses.color_pair(1) + curses.A_BOLD
  655. else:
  656. color = curses.color_pair(1)
  657. stdscr.insstr(0, i, "─", color)
  658. if bold[2]:
  659. color = curses.color_pair(9) + curses.A_BOLD
  660. else:
  661. color = curses.color_pair(9)
  662. stdscr.insstr(height - 1, i, " ", color)
  663. if archive:
  664. echoareas = nodes[node]["archive"]
  665. draw_title(0, 0, "Архив")
  666. else:
  667. echoareas = nodes[node]["echoareas"]
  668. draw_title(0, 0, "Конференция")
  669. draw_status(1, version)
  670. draw_status(len(version) + 2, nodes[node]["nodename"])
  671. for echo in echoareas:
  672. l = len(echo[1])
  673. if l > m:
  674. m = l
  675. if m > width - 38:
  676. m = width - 38
  677. dsc_lens.append(l)
  678. y = 0
  679. count = "Сообщений"
  680. unread = "Не прочитано"
  681. description = "Описание"
  682. if width < 80 or m == 0:
  683. m = len(unread) - 7
  684. hidedsc = True
  685. draw_title(0, width + 2 - m - len(count) - len(unread) - 1, count)
  686. draw_title(0, width - 8 - m - 1, unread)
  687. if not hidedsc:
  688. draw_title(0, width - len(description) - 2, description)
  689. for echo in echoareas:
  690. if y - start < height - 2:
  691. if y == cursor:
  692. if y >= start:
  693. if bold[2]:
  694. color = curses.color_pair(3) + curses.A_BOLD
  695. stdscr.attron (curses.color_pair(3))
  696. stdscr.attron (curses.A_BOLD)
  697. else:
  698. color = curses.color_pair(3)
  699. stdscr.attron (curses.color_pair(3))
  700. stdscr.attroff (curses.A_BOLD)
  701. draw_cursor(y - start, color)
  702. else:
  703. if y >= start:
  704. if bold[3]:
  705. color = curses.color_pair(4) + curses.A_BOLD
  706. else:
  707. color = curses.color_pair(4)
  708. draw_cursor(y - start, color)
  709. if bold[3]:
  710. stdscr.attron (curses.color_pair(4))
  711. stdscr.attron (curses.A_BOLD)
  712. else:
  713. stdscr.attron (curses.color_pair(4))
  714. stdscr.attroff (curses.A_BOLD)
  715. if y + 1 >= start + 1:
  716. if counts_rescan:
  717. counts = rescan_counts(echoareas)
  718. counts_rescan = False
  719. echo_length = int(counts[y][0])
  720. if echo[0] in lasts:
  721. last = lasts[echo[0]]
  722. else:
  723. last = -1
  724. if last < echo_length - 1 or last == -1 and echo_length == 1:
  725. stdscr.addstr(y + 1 - start, 0, "+")
  726. if last < 0:
  727. last = 0
  728. if echo[0] in nodes[node]["clone"]:
  729. stdscr.addstr(y + 1 - start, 1, "*")
  730. stdscr.addstr(y + 1 - start, 2, echo[0])
  731. if width >= 80:
  732. if width - 38 >= len(echo[1]):
  733. stdscr.addstr(y + 1 - start, width - 1 - dsc_lens[y], echo[1], color)
  734. else:
  735. cut_index = width - 38 - len(echo[1])
  736. stdscr.addstr(y + 1 - start, width - 1 - len(echo[1][:cut_index]), echo[1][:cut_index])
  737. stdscr.addstr(y + 1 - start, width - 10 - m - len(counts[y][0]), counts[y][0])
  738. stdscr.addstr(y + 1 - start, width - 2 - m - len(counts[y][1]), counts[y][1])
  739. y = y + 1
  740. current_time()
  741. stdscr.refresh()
  742. def find_new(cursor):
  743. ret = cursor
  744. n = 0
  745. lock = False
  746. for i in counts:
  747. n = n + 1
  748. if n > cursor and not lock and int(i[1]) > 0:
  749. ret = n - 1
  750. lock = True
  751. return ret
  752. def fetch_mail():
  753. curses.echo()
  754. curses.curs_set(True)
  755. curses.endwin()
  756. os.system('cls' if os.name == 'nt' else 'clear')
  757. echoareas = []
  758. to = ""
  759. if len(nodes[node]["to"]) > 0:
  760. to = "\"" + ",".join(nodes[node]["to"]) + "\""
  761. else:
  762. to = False
  763. d = "txt"
  764. if db == 1:
  765. d = "aio"
  766. elif db == 2:
  767. d = "ait"
  768. elif db == 3:
  769. d = "sqlite"
  770. for echoarea in nodes[node]["echoareas"][2:]:
  771. if not echoarea[2]:
  772. echoareas.append(echoarea[0])
  773. mailer(nodes[node]["clone"])
  774. nodes[node]["clone"] = []
  775. stdscr = curses.initscr()
  776. curses.start_color()
  777. curses.use_default_colors()
  778. curses.noecho()
  779. curses.curs_set(False)
  780. stdscr.keypad(True)
  781. get_term_size()
  782. def load_lasts():
  783. global lasts
  784. if os.path.exists("lasts.lst"):
  785. f = open("lasts.lst", "rb")
  786. lasts = pickle.load(f)
  787. f.close()
  788. def edit_config(out = False):
  789. curses.echo()
  790. curses.curs_set(True)
  791. curses.endwin()
  792. p = subprocess.Popen(editor + " ./caesium.cfg", shell=True)
  793. p.wait()
  794. reset_config()
  795. load_config()
  796. stdscr = curses.initscr()
  797. curses.start_color()
  798. curses.use_default_colors()
  799. curses.noecho()
  800. curses.curs_set(False)
  801. stdscr.keypad(True)
  802. get_term_size()
  803. def echo_selector():
  804. global echo_cursor, archive_cursor, counts, counts_rescan, next_echoarea, node
  805. archive = False
  806. echoareas = nodes[node]["echoareas"]
  807. key = 0
  808. go = True
  809. start = 0
  810. if archive:
  811. cursor = echo_cursor
  812. else:
  813. cursor = archive_cursor
  814. while go:
  815. draw_echo_selector(start, cursor, archive)
  816. key = stdscr.getch()
  817. if key == curses.KEY_RESIZE:
  818. get_term_size()
  819. if cursor >= height - 2:
  820. start = cursor - height + 3
  821. if cursor - start <= 0:
  822. start = cursor
  823. if start > 0 and height - 2 > len(echoareas):
  824. start = 0
  825. stdscr.clear()
  826. elif key in s_up and cursor > 0:
  827. cursor = cursor - 1
  828. if cursor - start < 0 and start > 0:
  829. start = start - 1
  830. elif key in s_down and cursor < len(echoareas) - 1:
  831. cursor = cursor + 1
  832. if cursor - start > height - 3 and start < len(echoareas) - height + 2:
  833. start = start + 1
  834. elif key in s_ppage:
  835. cursor = cursor - height + 2
  836. if cursor < 0:
  837. cursor = 0
  838. if cursor - start < 0 and start > 0:
  839. start = start - height + 2
  840. if start < 0:
  841. start = 0
  842. elif key in s_npage:
  843. cursor = cursor + height - 2
  844. if cursor >= len(echoareas):
  845. cursor = len(echoareas) - 1
  846. if cursor - start > height - 3:
  847. start = start + height - 2
  848. if start > len(echoareas) - height + 2:
  849. start = len(echoareas) - height + 2
  850. elif key in s_home:
  851. cursor = 0
  852. start = 0
  853. elif key in s_end:
  854. cursor = len(echoareas) - 1
  855. if len(echoareas) >= height - 2:
  856. start = len(echoareas) - height + 2
  857. elif key in s_get:
  858. fetch_mail()
  859. draw_message_box("Подождите", False)
  860. get_counts(True)
  861. stdscr.clear()
  862. counts = rescan_counts(echoareas)
  863. cursor = find_new(0)
  864. if cursor >= height - 2:
  865. start = cursor - height + 3
  866. if cursor - start <= 0:
  867. start = cursor
  868. elif key in s_archive and len(nodes[node]["archive"]) > 0:
  869. if archive:
  870. archive = False
  871. archive_cursor = cursor
  872. cursor = echo_cursor
  873. echoareas = nodes[node]["echoareas"]
  874. stdscr.clear()
  875. counts_rescan = True
  876. else:
  877. archive = True
  878. echo_cursor = cursor
  879. cursor = archive_cursor
  880. echoareas = nodes[node]["archive"]
  881. stdscr.clear()
  882. counts_rescan = True
  883. elif key in s_enter:
  884. draw_message_box("Подождите", False)
  885. if echoareas[cursor][0] in lasts:
  886. last = lasts[echoareas[cursor][0]]
  887. else:
  888. last = 0
  889. if cursor == 0:
  890. echo_length = len(get_favorites_list())
  891. elif cursor == 1:
  892. echo_length = len(get_carbonarea())
  893. else:
  894. echo_length = get_echo_length(echoareas[cursor][0])
  895. if last > 0 and last < echo_length:
  896. last = last + 1
  897. if last >= echo_length:
  898. last = echo_length
  899. if cursor == 1:
  900. go = not echo_reader(echoareas[cursor], last, archive, True, False, True)
  901. elif cursor == 0 or echoareas[cursor][2]:
  902. go = not echo_reader(echoareas[cursor], last, archive, True, False, False)
  903. else:
  904. go = not echo_reader(echoareas[cursor], last, archive, False, False, False)
  905. counts_rescan = True
  906. if next_echoarea:
  907. counts = rescan_counts(echoareas)
  908. cursor = find_new(cursor)
  909. if cursor - start > height - 3:
  910. start = cursor - height + 3
  911. next_echoarea = False
  912. elif key in s_out:
  913. out_length = get_out_length()
  914. if out_length > -1:
  915. go = not echo_reader("out", out_length, archive, False, True, False)
  916. elif key in s_drafts:
  917. out_length = get_out_length(True)
  918. if out_length > -1:
  919. go = not echo_reader("out", out_length, archive, False, True, False, True)
  920. elif key in s_nnode:
  921. archive = False
  922. node = node + 1
  923. if node == len(nodes):
  924. node = 0
  925. echoareas = nodes[node]["echoareas"]
  926. draw_message_box("Подождите", False)
  927. get_counts()
  928. stdscr.clear()
  929. counts_rescan = True
  930. cursor = 0
  931. start = 0
  932. elif key in s_pnode:
  933. archive = False
  934. node = node - 1
  935. if node == -1:
  936. node = len(nodes) - 1
  937. echoareas = nodes[node]["echoareas"]
  938. draw_message_box("Подождите", False)
  939. get_counts()
  940. stdscr.clear()
  941. counts_rescan = True
  942. cursor = 0
  943. start = 0
  944. elif key in s_clone:
  945. if cursor > 1 and not echoareas[cursor][2]:
  946. if echoareas[cursor][0] in nodes[node]["clone"]:
  947. nodes[node]["clone"].remove(echoareas[cursor][0])
  948. else:
  949. nodes[node]["clone"].append(echoareas[cursor][0])
  950. elif key in s_config:
  951. edit_config()
  952. reset_config()
  953. load_config()
  954. load_colors()
  955. get_counts()
  956. stdscr.clear()
  957. counts_rescan = True
  958. node = 0
  959. archive = False
  960. echoareas = nodes[node]["echoareas"]
  961. cursor = 0
  962. elif key in g_quit:
  963. go = False
  964. if archive:
  965. archive_cursor = cursor
  966. else:
  967. echo_cursor = cursor
  968. def read_out_msg(msgid):
  969. size = "0b"
  970. temp = open("out/" + nodes[node]["nodename"] + "/" + msgid, "r").read().split("\n")
  971. msg = []
  972. msg.append("")
  973. msg.append(temp[0])
  974. msg.append("")
  975. msg.append("")
  976. msg.append("")
  977. msg.append(temp[1])
  978. msg.append(temp[2])
  979. for line in temp[3:]:
  980. if not(line.startswith("@repto:")):
  981. msg.append(line)
  982. size = os.stat("out/" + nodes[node]["nodename"] + "/" + msgid).st_size
  983. if size < 1024:
  984. size = str(size) + " B"
  985. else:
  986. size = str(int(size / 1024 * 10) / 10) + " KB"
  987. return msg, size
  988. def body_render(tbody):
  989. body = ""
  990. code = ""
  991. sep = ""
  992. for i in range(0, width - 1):
  993. sep += "─"
  994. for line in tbody:
  995. n = 0
  996. rr = re.compile(r"^[a-zA-Zа-яА-Я0-9_\-.]{0,20}>{1,20}")
  997. cc = re.compile(r"(^\s*)(PS|P.S|ps|ЗЫ|З.Ы|\/\/|#)")
  998. try:
  999. count = line[0:rr.match(line).span()[1]].count(">")
  1000. except:
  1001. count = 0
  1002. if count > 0:
  1003. if count % 2 == 1:
  1004. code = chr(15)
  1005. elif count % 2 == 0:
  1006. code = chr(16)
  1007. elif cc.match(line):
  1008. code = chr(17)
  1009. elif line.startswith("== "):
  1010. code = chr(18)
  1011. else:
  1012. code = " "
  1013. if line == "----":
  1014. code = chr(17)
  1015. line = sep
  1016. if code != " " and code != chr(17) and code != chr(18):
  1017. line = " " + line
  1018. body = body + code
  1019. for word in line.split(" "):
  1020. if n + len(word) < width:
  1021. n = n + len(word)
  1022. body = body + word
  1023. if not word[-1:] == "\n":
  1024. n = n + 1
  1025. body = body + " "
  1026. else:
  1027. body = body[:-1]
  1028. if len(word) < width:
  1029. body = body + "\n" + code + word
  1030. n = len (word)
  1031. else:
  1032. chunks, chunksize = len(word), width - 1
  1033. chunk_list = [ word[i:i+chunksize] for i in range(0, chunks, chunksize) ]
  1034. for line in chunk_list:
  1035. body = body + "\n" + code + line
  1036. n = len(chunk_list[-1])
  1037. if not word[-1:] == "\n":
  1038. n = n + 1
  1039. body = body + " "
  1040. if body.endswith(" "):
  1041. body = body[:-1]
  1042. body = body + "\n"
  1043. return body.split("\n")
  1044. def draw_reader(echo, msgid, out):
  1045. for i in range(0, width):
  1046. if bold[0]:
  1047. color = curses.color_pair(1) + curses.A_BOLD
  1048. else:
  1049. color = curses.color_pair(1)
  1050. stdscr.insstr(0, i, "─", color)
  1051. stdscr.insstr(4, i, "─", color)
  1052. if bold[2]:
  1053. color = curses.color_pair(9) + curses.A_BOLD
  1054. else:
  1055. color = curses.color_pair(9)
  1056. stdscr.insstr(height - 1, i, " ", color)
  1057. if out:
  1058. draw_title(0, 0, echo)
  1059. if msgid.endswith(".out"):
  1060. ns = "не отправлено"
  1061. draw_title(4, width - len(ns) - 2, ns)
  1062. else:
  1063. if width >= 80:
  1064. draw_title(0, 0, echo + " / " + msgid)
  1065. else:
  1066. draw_title(0, 0, echo)
  1067. draw_status(1, version)
  1068. current_time()
  1069. for i in range(0, 3):
  1070. draw_cursor(i, 1)
  1071. if bold[1]:
  1072. color = curses.color_pair(2) + curses.A_BOLD
  1073. else:
  1074. color = curses.color_pair(2)
  1075. stdscr.addstr(1, 1, "От: ", color)
  1076. stdscr.addstr(2, 1, "Кому: ", color)
  1077. stdscr.addstr(3, 1, "Тема: ", color)
  1078. def call_editor(out = False, draft = False):
  1079. curses.echo()
  1080. curses.curs_set(True)
  1081. curses.endwin()
  1082. h = hashlib.sha1(str.encode(open("temp", "r",).read())).hexdigest()
  1083. p = subprocess.Popen(editor + " ./temp", shell=True)
  1084. p.wait()
  1085. stdscr = curses.initscr()
  1086. curses.start_color()
  1087. curses.use_default_colors()
  1088. curses.noecho()
  1089. curses.curs_set(False)
  1090. stdscr.keypad(True)
  1091. get_term_size()
  1092. if h != hashlib.sha1(str.encode(open("temp", "r",).read())).hexdigest():
  1093. d = menu("Куда сохранить?", ["Сохранить в исходящие", "Сохранить как черновик"])
  1094. if d:
  1095. if d == 2:
  1096. if not out:
  1097. save_out(True)
  1098. else:
  1099. if out.endswith(".out"):
  1100. try:
  1101. os.remove("out/" + nodes[node]["nodename"] + "/" + out)
  1102. except:
  1103. None
  1104. resave_out(out, True)
  1105. elif d == 1:
  1106. if not out:
  1107. save_out()
  1108. else:
  1109. if out.endswith(".draft"):
  1110. try:
  1111. os.remove("out/" + nodes[node]["nodename"] + "/" + out)
  1112. except:
  1113. None
  1114. resave_out(out.replace(".draft", ".out"))
  1115. else:
  1116. os.remove("temp")
  1117. def draw_message_box(smsg, wait):
  1118. maxlen = 0
  1119. msg = smsg.split("\n")
  1120. for line in msg:
  1121. if len(line) > maxlen:
  1122. maxlen = len(line)
  1123. if wait:
  1124. msgwin = curses.newwin(len(msg) + 4, maxlen + 2, int(height / 2 - 2) , int(width / 2 - maxlen / 2 - 2))
  1125. else:
  1126. msgwin = curses.newwin(len(msg) + 2, maxlen + 2, int(height / 2 - 2) , int(width / 2 - maxlen / 2 - 2))
  1127. if bold[0]:
  1128. msgwin.attron(curses.color_pair(1))
  1129. msgwin.attron(curses.A_BOLD)
  1130. else:
  1131. msgwin.attron(curses.color_pair(1))
  1132. msgwin.bkgd(' ', curses.color_pair(1))
  1133. msgwin.border()
  1134. i = 1
  1135. if bold[3]:
  1136. color = curses.color_pair(4) + curses.A_BOLD
  1137. else:
  1138. color = curses.color_pair(4)
  1139. for line in msg:
  1140. msgwin.addstr(i, 1, line, color)
  1141. i = i + 1
  1142. if bold[1]:
  1143. color = curses.color_pair(2) + curses.A_BOLD
  1144. else:
  1145. color = curses.color_pair(2)
  1146. if wait:
  1147. msgwin.addstr(len(msg) + 2, int((maxlen + 2 - 21) / 2), "Нажмите любую клавишу", color)
  1148. msgwin.refresh()
  1149. def message_box(smsg):
  1150. draw_message_box(smsg, True)
  1151. stdscr.getch()
  1152. stdscr.clear()
  1153. def save_message_to_file(msgid, echoarea):
  1154. msg, size = read_msg(msgid, echoarea)
  1155. f = open(msgid + ".txt", "w")
  1156. f.write("== " + msg[1] + " ==================== " + str(msgid) + "\n")
  1157. f.write("От: " + msg[3] + " (" + msg[4] + ")\n")
  1158. f.write("Кому: " + msg[5] + "\n")
  1159. f.write("Тема: " + msg[6] + "\n")
  1160. f.write("\n".join(msg[7:]))
  1161. f.close
  1162. message_box("Сообщение сохранено в файл\n" + str(msgid) + ".txt")
  1163. def get_out_msgids(drafts = False):
  1164. msgids = []
  1165. not_sended = []
  1166. if os.path.exists("out/" + nodes[node]["nodename"]):
  1167. if drafts:
  1168. msgids = [f for f in sorted(os.listdir("out/" + nodes[node]["nodename"])) if f.endswith(".draft")]
  1169. else:
  1170. msgids = [f for f in sorted(os.listdir("out/" + nodes[node]["nodename"])) if f.endswith(".out") or f.endswith(".outmsg")]
  1171. return msgids
  1172. def quote(to):
  1173. if oldquote == True:
  1174. return ""
  1175. else:
  1176. if len(to) == 1:
  1177. q = to[0]
  1178. else:
  1179. q = ""
  1180. for word in to:
  1181. q = q + word[0]
  1182. return q
  1183. def show_subject(subject):
  1184. if len(subject) > width - 8:
  1185. msg = ""
  1186. line = ""
  1187. for word in subject.split(" "):
  1188. if len(line + word) <= width - 4:
  1189. line = line + word + " "
  1190. else:
  1191. msg = msg + line + "\n"
  1192. line = word + " "
  1193. msg = msg + line
  1194. message_box(msg)
  1195. def calc_scrollbar_size(length):
  1196. if length > 0:
  1197. scrollbar_size = round((height - 6) * (height - 6) / length + 0.49)
  1198. if scrollbar_size < 1:
  1199. scrollbar_size = 1
  1200. else:
  1201. scrollbar_size = 1
  1202. return scrollbar_size
  1203. def set_attr(str):
  1204. if str == chr(15):
  1205. stdscr.attron(curses.color_pair(5))
  1206. if bold[4]:
  1207. stdscr.attron(curses.A_BOLD)
  1208. else:
  1209. stdscr.attroff(curses.A_BOLD)
  1210. elif str == chr(16):
  1211. stdscr.attron(curses.color_pair(6))
  1212. if bold[5]:
  1213. stdscr.attron(curses.A_BOLD)
  1214. else:
  1215. stdscr.attroff(curses.A_BOLD)
  1216. elif str == chr(17):
  1217. stdscr.attron(curses.color_pair(7))
  1218. if bold[6]:
  1219. stdscr.attron(curses.A_BOLD)
  1220. else:
  1221. stdscr.attroff(curses.A_BOLD)
  1222. elif str == chr(18):
  1223. stdscr.attron(curses.color_pair(10))
  1224. if bold[9]:
  1225. stdscr.attron(curses.A_BOLD)
  1226. else:
  1227. stdscr.attroff(curses.A_BOLD)
  1228. else:
  1229. stdscr.attron(curses.color_pair(4))
  1230. if bold[3]:
  1231. stdscr.attron(curses.A_BOLD)
  1232. else:
  1233. stdscr.attroff(curses.A_BOLD)
  1234. def get_msg(msgid):
  1235. r = urllib.request.Request(nodes[node]["node"] + "u/m/" + msgid)
  1236. with urllib.request.urlopen(r) as f:
  1237. bundle = f.read().decode("utf-8").split("\n")
  1238. for msg in bundle:
  1239. if msg:
  1240. m = msg.split(":")
  1241. msgid = m[0]
  1242. if len(msgid) == 20 and m[1]:
  1243. msgbody = base64.b64decode(m[1].encode("ascii")).decode("utf8")
  1244. if len(nodes[node]["to"]) > 0:
  1245. carbonarea = get_carbonarea()
  1246. if msgbody.split("\n")[5] in nodes[node]["to"] and not msgid in carbonarea:
  1247. add_to_carbonarea(msgid, msbbody)
  1248. save_message(msgid, msgbody)
  1249. def menu(title, items):
  1250. h = len(items)
  1251. w = 0
  1252. for item in items:
  1253. if len(item) > w:
  1254. w = len(item)
  1255. if len(item) > width - 2:
  1256. item = item[:width - 1]
  1257. if w >= width - 3:
  1258. w = width - 3
  1259. e = "Esc - отмена"
  1260. if w < len(title):
  1261. w = len(title) + 2
  1262. menu_win = curses.newwin(h + 2, w + 2, int(height / 2 - h / 2 - 2) , int(width / 2 - w / 2 - 2))
  1263. if bold[0]:
  1264. menu_win.attron(curses.color_pair(1))
  1265. menu_win.attron(curses.A_BOLD)
  1266. else:
  1267. menu_win.attron(curses.color_pair(1))
  1268. menu_win.border()
  1269. if bold[0]:
  1270. color = curses.color_pair(1) + curses.A_BOLD
  1271. else:
  1272. color = curses.color_pair(1)
  1273. menu_win.addstr(0, 1, "[", color)
  1274. menu_win.addstr(0, 2 + len(title), "]", color)
  1275. menu_win.addstr(h + 1, 1, "[", color)
  1276. menu_win.addstr(h + 1, 2 + len(e), "]", color)
  1277. if bold[1]:
  1278. color = curses.color_pair(2) + curses.A_BOLD
  1279. else:
  1280. color = curses.color_pair(2)
  1281. menu_win.addstr(0, 2, title, curses.color_pair(2) + curses.A_BOLD)
  1282. menu_win.addstr(h + 1, 2, e, curses.color_pair(2) + curses.A_BOLD)
  1283. if bold[0]:
  1284. color = curses.color_pair(1) + curses.A_BOLD
  1285. else:
  1286. color = curses.color_pair(1)
  1287. y = 1
  1288. quit = False
  1289. cancel = False
  1290. while not quit:
  1291. i = 1
  1292. for item in items:
  1293. if i == y :
  1294. if bold[2]:
  1295. color = curses.color_pair(3) + curses.A_BOLD
  1296. else:
  1297. color = curses.color_pair(3)
  1298. for x in range(1, w + 1):
  1299. menu_win.addstr(i, x, " ", color)
  1300. else:
  1301. if bold[3]:
  1302. color = curses.color_pair(4) + curses.A_BOLD
  1303. else:
  1304. color = curses.color_pair(4)
  1305. for x in range(1, w + 1):
  1306. menu_win.addstr(i, x, " ", color)
  1307. if len(item) < w - 2:
  1308. menu_win.addstr(i, 1, item, color)
  1309. else:
  1310. menu_win.addstr(i, 1, item[:w], color)
  1311. i = i + 1
  1312. menu_win.refresh()
  1313. key = stdscr.getch()
  1314. if key in r_up:
  1315. if y > 1:
  1316. y -= 1
  1317. else:
  1318. y = h
  1319. elif key in r_down:
  1320. if y < h:
  1321. y += 1
  1322. else:
  1323. y = 1
  1324. elif key in s_enter:
  1325. quit = True
  1326. elif key in r_quit:
  1327. cancel = True
  1328. quit = True
  1329. if cancel:
  1330. return False
  1331. else:
  1332. return y
  1333. def open_link(link):
  1334. global browser
  1335. browser.open(link)
  1336. def get_out(drafts=False):
  1337. if drafts:
  1338. return get_out_msgids(True)
  1339. else:
  1340. return get_out_msgids()
  1341. def echo_reader(echo, last, archive, favorites, out, carbonarea, drafts = False):
  1342. global lasts, next_echoarea
  1343. stdscr.clear()
  1344. if bold[0]:
  1345. stdscr.attron(curses.color_pair(1))
  1346. stdscr.attron(curses.A_BOLD)
  1347. else:
  1348. stdscr.attron(curses.color_pair(1))
  1349. y = 0
  1350. msgn = last
  1351. key = 0
  1352. if drafts:
  1353. msgids = get_out_msgids(True)
  1354. elif out:
  1355. msgids = get_out_msgids()
  1356. elif favorites and not carbonarea:
  1357. msgids = get_favorites_list()
  1358. elif carbonarea:
  1359. msgids = get_carbonarea()
  1360. else:
  1361. msgids = get_echo_msgids(echo[0])
  1362. if msgn > len(msgids) - 1:
  1363. msgn = len(msgids) - 1
  1364. if len(msgids) > 0:
  1365. if drafts:
  1366. msg, size = read_out_msg(msgids[msgn])
  1367. elif out:
  1368. msg, size = read_out_msg(msgids[msgn])
  1369. else:
  1370. msg, size = read_msg(msgids[msgn], echo[0])
  1371. else:
  1372. msg = ["", "", "", "", "", "", "", "", "Сообщение отсутствует в базе"]
  1373. size = "0b"
  1374. msgbody = body_render(msg[8:])
  1375. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1376. go = True
  1377. stack = []
  1378. while go:
  1379. if len(msgids) > 0:
  1380. draw_reader(msg[1], msgids[msgn], out)
  1381. if width >= 80:
  1382. msg_string = "Сообщение " + str(msgn + 1) + " из " + str(len(msgids)) + " (" + str(len(msgids) - msgn - 1) + " осталось)"
  1383. else:
  1384. msg_string = str(msgn + 1) + "/" + str(len(msgids)) + " [" + str(len(msgids) - msgn - 1) + "]"
  1385. draw_status(len(version) + 2, msg_string)
  1386. if drafts:
  1387. dsc = "Черновики"
  1388. elif out:
  1389. dsc = "Исходящие"
  1390. else:
  1391. dsc = echo[1]
  1392. if len(dsc) > 0 and width >= 80:
  1393. draw_title(0, width - 2 - len(dsc), dsc)
  1394. if not(out):
  1395. try:
  1396. if width >= 80:
  1397. msgtime = time.strftime("%d %b %Y %H:%M UTC", time.gmtime(int(msg[2])))
  1398. else:
  1399. msgtime = time.strftime("%d.%m.%y %H:%M", time.gmtime(int(msg[2])))
  1400. except:
  1401. msgtime = ""
  1402. if bold[3]:
  1403. color = curses.color_pair(4) + curses.A_BOLD
  1404. else:
  1405. color = curses.color_pair(4)
  1406. if not(out):
  1407. if width >= 80:
  1408. stdscr.addstr(1, 7, msg[3] + " (" + msg[4] + ")", color)
  1409. else:
  1410. stdscr.addstr(1, 7, msg[3], color)
  1411. stdscr.addstr(1, width - len(msgtime) - 1, msgtime, color)
  1412. else:
  1413. if len(nodes[node]["to"]) > 0:
  1414. stdscr.addstr(1, 7, nodes[node]["to"][0], color)
  1415. stdscr.addstr(2, 7, msg[5], color)
  1416. stdscr.addstr(3, 7, msg[6][:width - 8], color)
  1417. draw_title(4, 0, size)
  1418. tags = msg[0].split("/")
  1419. if "repto" in tags and 36 + len(size) < width:
  1420. repto = tags[tags.index("repto") + 1]
  1421. draw_title(4, len(size) + 3, "Ответ на " + repto)
  1422. else:
  1423. repto = False
  1424. for i in range (0, height - 6):
  1425. for x in range (0, width):
  1426. stdscr.addstr(i + 5, x, " ", 1)
  1427. if i < len(msgbody) - 1:
  1428. if y + i < len(msgbody) and len(msgbody[y+i]) > 0:
  1429. set_attr(msgbody[y + i][0])
  1430. x = 0
  1431. for word in msgbody[y + i][1:].split(" "):
  1432. if word.startswith("http://") or word.startswith("https://") or word.startswith("ftp://"):
  1433. stdscr.attron(curses.color_pair(8))
  1434. if bold[7]:
  1435. stdscr.attron(curses.A_BOLD)
  1436. else:
  1437. stdscr.attroff(curses.A_BOLD)
  1438. stdscr.addstr(i + 5, x, word)
  1439. set_attr(msgbody[y + i][0])
  1440. else:
  1441. stdscr.addstr(i + 5, x, word)
  1442. x += len(word) + 1
  1443. stdscr.attron(curses.color_pair(11))
  1444. if bold[10]:
  1445. stdscr.attron(curses.A_BOLD)
  1446. else:
  1447. stdscr.attroff(curses.A_BOLD)
  1448. if len(msgbody) > height - 5:
  1449. for i in range(5, height - 1):
  1450. stdscr.addstr(i, width - 1, "░")
  1451. scrollbar_y = round(y * (height - 6) / len(msgbody) + 0.49)
  1452. if scrollbar_y < 0:
  1453. scrollbar_y = 0
  1454. elif scrollbar_y > height - 6 - scrollbar_size or y >= len(msgbody) - (height - 6):
  1455. scrollbar_y = height - 6 - scrollbar_size
  1456. for i in range(scrollbar_y + 5, scrollbar_y + 5 + scrollbar_size):
  1457. if i < height - 1:
  1458. stdscr.addstr(i, width - 1, "█")
  1459. else:
  1460. draw_reader(echo[0], "", out)
  1461. stdscr.attron(curses.color_pair(1))
  1462. if bold[0]:
  1463. stdscr.attron(curses.A_BOLD)
  1464. else:
  1465. stdscr.attroff(curses.A_BOLD)
  1466. stdscr.refresh()
  1467. key = stdscr.getch()
  1468. if key == curses.KEY_RESIZE:
  1469. y = 0
  1470. get_term_size()
  1471. if len(msgids) > 0:
  1472. msgbody = body_render(msg[8:])
  1473. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1474. stdscr.clear()
  1475. elif key in r_prev and msgn > 0:
  1476. y = 0
  1477. if len(msgids) > 0:
  1478. msgn = msgn - 1
  1479. if len(stack) > 0:
  1480. stack = []
  1481. if out:
  1482. msg, size = read_out_msg(msgids[msgn])
  1483. else:
  1484. msg, size = read_msg(msgids[msgn], echo[0])
  1485. msgbody = body_render(msg[8:])
  1486. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1487. elif key in r_next and msgn < len(msgids) - 1:
  1488. y = 0
  1489. if len(msgids) > 0:
  1490. msgn = msgn +1
  1491. if len(stack) > 0:
  1492. stack = []
  1493. if out:
  1494. msg, size = read_out_msg(msgids[msgn])
  1495. else:
  1496. msg, size = read_msg(msgids[msgn], echo[0])
  1497. msgbody = body_render(msg[8:])
  1498. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1499. elif key in r_next and (msgn == len(msgids) - 1 or len(msgids) == 0):
  1500. go = False
  1501. quit = False
  1502. next_echoarea = True
  1503. elif key in r_prep and not echo[0] == "carbonarea" and not echo[0] == "favorites" and not out and repto:
  1504. if repto in msgids:
  1505. stack.append(msgn)
  1506. msgn = msgids.index(repto)
  1507. msg, size = read_msg(msgids[msgn], echo[0])
  1508. msgbody = body_render(msg[8:])
  1509. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1510. elif key in r_nrep and not out and len(stack) > 0:
  1511. msgn = stack.pop()
  1512. msg, size = read_msg(msgids[msgn], echo[0])
  1513. msgbody = body_render(msg[8:])
  1514. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1515. elif key in r_up and y > 0:
  1516. if len(msgids) > 0:
  1517. y = y - 1
  1518. elif key in r_ppage:
  1519. if len(msgids) > 0:
  1520. y = y - height + 6
  1521. if y < 0:
  1522. y = 0
  1523. elif key in r_npage:
  1524. if y < len(msgbody) - height + 5:
  1525. if len(msgids) > 0 and len(msgbody) > height - 5:
  1526. y = y + height - 6
  1527. elif key in r_home:
  1528. if len(msgids) > 0:
  1529. y = 0
  1530. elif key in r_mend:
  1531. if len(msgids) > 0 and len(msgbody) > height - 5:
  1532. y = len(msgbody) - height + 5
  1533. elif key in r_ukeys:
  1534. if len(msgids) == 0 or y >= len(msgbody) - height + 5:
  1535. y = 0
  1536. if msgn == len(msgids) - 1 or len(msgids) == 0:
  1537. next_echoarea = True
  1538. go = False
  1539. quit = False
  1540. else:
  1541. msgn = msgn +1
  1542. if len(stack) > 0:
  1543. stack = []
  1544. if out:
  1545. msg, size = read_out_msg(msgids[msgn])
  1546. else:
  1547. msg, size = read_msg(msgids[msgn], echo[0])
  1548. msgbody = body_render(msg[8:])
  1549. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1550. else:
  1551. if len(msgids) > 0 and len(msgbody) > height - 5:
  1552. y = y + height - 6
  1553. elif key in r_down:
  1554. if len(msgids) > 0:
  1555. if y + height - 5 < len(msgbody):
  1556. y = y + 1
  1557. elif key in r_begin:
  1558. if len(msgids) > 0:
  1559. y = 0
  1560. msgn = 0
  1561. if len(stack) > 0:
  1562. stack = []
  1563. if out:
  1564. msg, size = read_out_msg(msgids[msgn])
  1565. else:
  1566. msg, size = read_msg(msgids[msgn], echo[0])
  1567. msgbody = body_render(msg[8:])
  1568. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1569. elif key in r_end:
  1570. if len(msgids) > 0:
  1571. y = 0
  1572. msgn = len(msgids) - 1
  1573. if len(stack) > 0:
  1574. stack = []
  1575. if out:
  1576. msg, size = read_out_msg(msgids[msgn])
  1577. else:
  1578. msg, size = read_msg(msgids[msgn], echo[0])
  1579. msgbody = body_render(msg[8:])
  1580. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1581. elif (key in r_ins) and not archive and not out:
  1582. if not favorites:
  1583. f = open("temp", "w")
  1584. f.write(echo[0] + "\n")
  1585. f.write("All\n")
  1586. f.write("No subject\n\n")
  1587. f.close()
  1588. call_editor()
  1589. stdscr.clear()
  1590. elif key in r_save and not out:
  1591. save_message_to_file(msgids[msgn], echo[0])
  1592. elif key in r_favorites and not out:
  1593. saved = save_to_favorites(msgids[msgn], msg)
  1594. draw_message_box("Подождите", False)
  1595. get_counts(False, True)
  1596. if saved:
  1597. message_box("Собщение добавлено в избранные")
  1598. else:
  1599. message_box("Собщение уже есть в избранных")
  1600. elif (key in r_quote) and not archive and not out:
  1601. if len(msgids) > 0:
  1602. f = open("temp", "w")
  1603. f.write(msgids[msgn] + "\n")
  1604. f.write(msg[1] + "\n")
  1605. f.write(msg[3] + "\n")
  1606. to = msg[3].split(" ")
  1607. q = quote(to)
  1608. if not msg[6].startswith("Re:"):
  1609. f.write("Re: " + msg[6] + "\n")
  1610. else:
  1611. f.write(msg[6] + "\n")
  1612. rr = re.compile(r"^[a-zA-Zа-яА-Я0-9_-]{0,20}>{1,20}")
  1613. for line in msg[8:]:
  1614. if line.strip() != "":
  1615. if rr.match(line):
  1616. if line[rr.match(line).span()[1]] == " ":
  1617. quoter = ">"
  1618. else:
  1619. quoter = "> "
  1620. f.write("\n" + line[:rr.match(line).span()[1]] + quoter + line[rr.match(line).span()[1]:])
  1621. else:
  1622. f.write("\n" + q + "> " + line)
  1623. else:
  1624. f.write("\n" + line)
  1625. f.close()
  1626. call_editor()
  1627. elif key in r_subj:
  1628. show_subject(msg[6])
  1629. elif key in r_info and not out and width < 80:
  1630. message_box("id : " + msgids[msgn] + "\naddr: " + msg[4])
  1631. elif key in o_edit and out:
  1632. if msgids[msgn].endswith(".out") or msgids[msgn].endswith(".draft"):
  1633. copyfile("out/" + nodes[node]["nodename"] + "/" + msgids[msgn], "temp")
  1634. if drafts:
  1635. call_editor(msgids[msgn], drafts)
  1636. msgids = get_out(True)
  1637. else:
  1638. call_editor(msgids[msgn])
  1639. msgids = get_out()
  1640. if msgn > len(msgids) - 1:
  1641. msgn = len(msgids) -1
  1642. if len(msgids) > 0:
  1643. msg, size = read_out_msg(msgids[msgn])
  1644. msgbody = body_render(msg[8:])
  1645. else:
  1646. go = False
  1647. quit = False
  1648. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1649. stdscr.clear()
  1650. else:
  1651. message_box("Сообщение уже отправлено")
  1652. stdscr.clear()
  1653. elif key in f_delete and favorites and not carbonarea:
  1654. if len(msgids) > 0:
  1655. remove_from_favorites(msgids[msgn])
  1656. draw_message_box("Подождите", False)
  1657. get_counts(False, True)
  1658. msgids = get_echo_msgids(echo[0])
  1659. if len(msgids) > 0:
  1660. if msgn >= len(msgids):
  1661. msgn = len(msgids) - 1
  1662. msg, size = read_msg(msgids[msgn], echo[0])
  1663. msgbody = body_render(msg[8:])
  1664. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1665. else:
  1666. msgbody = []
  1667. stdscr.clear()
  1668. elif key in r_getmsg and size == "0b":
  1669. try:
  1670. get_msg(msgids[msgn])
  1671. draw_message_box("Подождите", False)
  1672. get_counts(True, False)
  1673. stdscr.clear()
  1674. msg, size = read_msg(msgids[msgn], echo[0])
  1675. msgbody = body_render(msg[8:])
  1676. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1677. except:
  1678. message_box("Не удалось определить msgid.")
  1679. stdscr.clear()
  1680. elif key in r_links:
  1681. results = urltemplate.findall("\n".join(msg[8:]))
  1682. links = []
  1683. for item in results:
  1684. links.append(item[0])
  1685. if len(links) == 1:
  1686. open_link(links[0])
  1687. else:
  1688. i = menu("Выберите ссылку", links)
  1689. if i:
  1690. open_link(links[i - 1])
  1691. stdscr.clear()
  1692. elif key in r_to_out and drafts:
  1693. copyfile("out/" + nodes[node]["nodename"] + "/" + msgids[msgn], "out/" + nodes[node]["nodename"] + "/" + msgids[msgn].replace(".draft", ".out"))
  1694. os.remove("out/" + nodes[node]["nodename"] + "/" + msgids[msgn])
  1695. if drafts:
  1696. msgids = get_out(True)
  1697. else:
  1698. msgids = get_out()
  1699. if msgn > len(msgids) - 1:
  1700. msgn = len(msgids) -1
  1701. if len(msgids) > 0:
  1702. msg, size = read_out_msg(msgids[msgn])
  1703. msgbody = body_render(msg[8:])
  1704. else:
  1705. go = False
  1706. quit = False
  1707. elif key in r_to_drafts and out and not drafts and msgids[msgn].endswith(".out"):
  1708. copyfile("out/" + nodes[node]["nodename"] + "/" + msgids[msgn], "out/" + nodes[node]["nodename"] + "/" + msgids[msgn].replace(".out", ".draft"))
  1709. os.remove("out/" + nodes[node]["nodename"] + "/" + msgids[msgn])
  1710. if drafts:
  1711. msgids = get_out(True)
  1712. else:
  1713. msgids = get_out()
  1714. if msgn > len(msgids) - 1:
  1715. msgn = len(msgids) -1
  1716. if len(msgids) > 0:
  1717. msg, size = read_out_msg(msgids[msgn])
  1718. msgbody = body_render(msg[8:])
  1719. else:
  1720. go = False
  1721. quit = False
  1722. elif key in r_list and not out and not drafts:
  1723. if db == 0:
  1724. message_box("Функция не поддерживается текстовой базой.")
  1725. else:
  1726. l = msg_list(echo, msgids, msgn)
  1727. if l > -1:
  1728. y = 0
  1729. msgn = l
  1730. if len(stack) > 0:
  1731. stack = []
  1732. msg, size = read_msg(msgids[msgn], echo[0])
  1733. msgbody = body_render(msg[8:])
  1734. scrollbar_size = calc_scrollbar_size(len(msgbody))
  1735. elif key in r_quit:
  1736. go = False
  1737. quit = False
  1738. next_echoarea = False
  1739. elif key in g_quit:
  1740. go = False
  1741. quit = True
  1742. lasts[echo[0]] = msgn
  1743. f = open("lasts.lst", "wb")
  1744. pickle.dump(lasts, f)
  1745. f.close()
  1746. stdscr.clear()
  1747. return quit
  1748. def draw_msg_list(echo, lst, msgn):
  1749. stdscr.clear()
  1750. for i in range(0, width):
  1751. if bold[0]:
  1752. color = curses.color_pair(1) + curses.A_BOLD
  1753. else:
  1754. color = curses.color_pair(1)
  1755. stdscr.insstr(0, i, "─", color)
  1756. if width >= 80:
  1757. draw_title(0, 0, "Список сообщений в конференции " + echo)
  1758. else:
  1759. draw_title(0, 0, echo)
  1760. def msg_list(echoarea, msgids, msgn):
  1761. lst = []
  1762. lst = get_msg_list_data(echoarea[0])
  1763. draw_msg_list(echoarea[0], lst, msgn)
  1764. echo_length = len(lst)
  1765. if echo_length <= height - 1:
  1766. start = 0
  1767. end = echo_length - 1
  1768. elif msgn + height - 1 < echo_length:
  1769. start = msgn
  1770. end = msgn + height - 1
  1771. else:
  1772. start = echo_length - height + 1
  1773. end = start + height - 1
  1774. quit = False
  1775. cancel = False
  1776. y = msgn - start
  1777. while not quit:
  1778. n = 1
  1779. for i in range(start, end):
  1780. if i == y + start:
  1781. if bold[2]:
  1782. color = curses.color_pair(3) + curses.A_BOLD
  1783. else:
  1784. color = curses.color_pair(3)
  1785. else:
  1786. if bold[3]:
  1787. color = curses.color_pair(4) + curses.A_BOLD
  1788. else:
  1789. color = curses.color_pair(4)
  1790. draw_cursor(n - 1, color)
  1791. stdscr.addstr(n, 0, lst[i][1], color)
  1792. stdscr.addstr(n, 16, lst[i][2][:width-26], color)
  1793. stdscr.insstr(n, width - 10, lst[i][3], color)
  1794. n += 1
  1795. key = stdscr.getch()
  1796. if key in s_up:
  1797. y = y - 1
  1798. if start > 0 and y + start < start:
  1799. start -= 1
  1800. end -= 1
  1801. if y == -1:
  1802. y = 0
  1803. elif key in s_down:
  1804. y = y + 1
  1805. if y + start + 1 > end and y + start < echo_length:
  1806. start += 1
  1807. end += 1
  1808. if y > height - 2:
  1809. y = height - 2
  1810. elif key in s_ppage:
  1811. if y == 0:
  1812. start = start - height + 1
  1813. if start < 0:
  1814. start = 0
  1815. end = start + height - 1
  1816. if y > 0:
  1817. y = 0
  1818. elif key in s_npage:
  1819. if y == height - 2:
  1820. start = start + height - 1
  1821. if start > echo_length - height + 1:
  1822. start = echo_length - height + 1
  1823. end = start + height - 1
  1824. if y < height - 2:
  1825. y = height - 2
  1826. elif key in s_home:
  1827. y = 0
  1828. start = 0
  1829. end = height - 1
  1830. elif key in s_end:
  1831. y = height - 2
  1832. start = echo_length - height + 1
  1833. end = start + height - 1
  1834. elif key in s_enter:
  1835. quit = True
  1836. elif key in r_quit:
  1837. quit = True
  1838. cancel = True
  1839. if cancel:
  1840. return -1
  1841. else:
  1842. return y + start
  1843. loc = locale.getdefaultlocale()
  1844. locale.setlocale(locale.LC_ALL, loc[0] + "." + loc[1])
  1845. check_config()
  1846. reset_config()
  1847. load_config()
  1848. if db == 0:
  1849. from api.txt import *
  1850. elif db == 1:
  1851. from api.aio import *
  1852. elif db == 2:
  1853. from api.ait import *
  1854. elif db == 3:
  1855. from api.sqlite import *
  1856. check_directories()
  1857. load_lasts()
  1858. stdscr = curses.initscr()
  1859. curses.start_color()
  1860. curses.use_default_colors()
  1861. load_colors()
  1862. curses.noecho()
  1863. curses.curs_set(False)
  1864. stdscr.keypad(True)
  1865. stdscr.bkgd(" ", curses.color_pair(1))
  1866. get_term_size()
  1867. if show_splash:
  1868. splash_screen()
  1869. draw_message_box("Подождите", False)
  1870. get_counts()
  1871. stdscr.clear()
  1872. echo_selector()
  1873. curses.echo()
  1874. curses.curs_set(True)
  1875. curses.endwin()