
    jjC'                    L   S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKrSSK	r
SSKr
SSKJrJrJr  SrSrSrSrSS	 jrSS
 jrSS jrSS jrSS jrSS jrSS jrSSS jjrS S jr\R8                  " S5      rS!S jrS S jrS"S jr S#S S jjr!\"S:X  a  \#" \!" 5       5      eg)$u  Идеи Наташи из TickTick: собрать и закрыть.

Раньше сбор жил двумя копиями inline-питона внутри planerka.sh и morning.sh —
и копии успели разойтись: утро фильтрует идеи по дате создания (ему нужны
сутки), планёрка берёт список целиком (раз в неделю это уместно). Разошлись
они по делу, но парсинг ответа, обход списков и обработка сбоев у них были
одинаковые, и правились бы в двух местах. Здесь разница осталась одним
флагом `--since-hours`, а всё остальное — общее.

Только stdlib: на Pi у планёрки нет venv.

Команды:
  list     --lists "Канал:pid,Threads:pid" [--since-hours 24] [--with-markers]
  complete --from-file <файл со строками, где есть pid=... id=...>

`list` печатает по строке на идею и всегда выходит нулём: идеи — приятное
дополнение к прогону, а не его условие, и отвалившийся TickTick не повод
ронять планёрку. Всё, что пошло не так, уходит в stderr — его скрипты
складывают в свой лог.

`complete` выходит ненулём, если хоть одну задачу закрыть не удалось: вот
здесь тишина недопустима — закрытая в TickTick, но не доехавшая до Наташи
идея пропадает отовсюду.
    )annotationsN)datetime	timedeltatimezonez https://api.ticktick.com/open/v1         c                 Z    [         R                  R                  S5      =(       d    [        $ )NTICKTICK_API_BASE)osenvirongetDEFAULT_BASE     &/home/rasp/content-planner/ticktick.py_baser   +   s    ::>>-.>,>r   c                 B    [         R                  R                  SS5      $ )NTICKTICK_ACCESS_TOKEN )r   r   r   r   r   r   _tokenr   /   s    ::>>1266r   c                R    [        SR                  U 5      [        R                  S9  g )Nzticktick: {})file)printformatsysstderr)msgs    r   warnr   3   s    	.


$3::6r   c                    [         R                  R                  U SSU-   0S9n[         R                  R                  U[        S9 nUR                  5       R                  S5      sS S S 5        $ ! , (       d  f       g = f)NAuthorizationBearer )headerstimeoututf-8)urllibrequestRequesturlopenTIMEOUTreaddecodeurltokenreqresps       r   http_getr3   9   s^    
..
 
 	E@Q.R
 
SC			W		5yy{!!'* 
6	5	5s   A//
A=c                    [         R                  R                  U SSSU-   0SS9n[         R                  R                  U[        S9 nUR                  5       sS S S 5        $ ! , (       d  f       g = f)Nr   r!   r"   POST)datar#   methodr$   )r'   r(   r)   r*   r+   getcoder.   s       r   	http_postr9   ?   s^    
..
 
 #U1BCF ! C 
		W		5||~ 
6	5	5s   A""
A0c                8   / nU =(       d    SR                  S5       Hy  nUR                  5       nU(       a  SU;  a  M"  UR                  S5      u  p4nUR                  5       UR                  5       pSU(       d  M^  U(       d  Mg  UR                  X545        M{     U$ )uF  «Канал:pid,Threads:pid» → [(имя, pid), ...].

Имя может содержать что угодно кроме запятой, pid — то, что после
последнего двоеточия: имена списков Наташа пишет по-русски и правит их
в .env руками.
r   ,:)splitstrip
rpartitionappend)rawoutentryname_pids         r   parse_listsrG   G   s     C)""3'5('',JJL#))+c4CCJJ{# ( Jr   c                    U R                  S5      =(       d    SR                  5       nU(       d  gUR                  SS5      nS H  n [        R                  " X5      s  $    g! [
         a     M,  f = f)us   Дата создания задачи или None, если её нет или она не разобралась.createdTimer   NZz+0000)z%Y-%m-%dT%H:%M:%S.%f%zz%Y-%m-%dT%H:%M:%S%z)r   r>   replacer   strptime
ValueError)taskrA   fmts      r   
created_atrP   Z   sn    88M"(b
/
/
1C
++c7
#C@	$$S.. A
   		s   	A$$
A21A2c                    [         R                  " U 5      n[	        U[
        5      (       a  UR                  S5      OSn[	        U[        5      (       d  / $ SnUbB  U=(       d$    [        R                  " [        R                  5      nU[        [        U5      S9-
  n/ nU H  n[	        U[
        5      (       d  M  UR                  S5      =(       d    SR                  SS5      R                  5       nU(       d  M[  Ub  [!        U5      n	U	b  X:  a  Ms  UR#                  U5        M     U$ ! [        [        4 a    / s $ f = f)uw  Задачи из ответа TickTick, при since_hours — только свежие.

Задачу без даты создания при фильтре не показываем: иначе старьё из
бэклога поехало бы в утренний блок каждый день, а утро — про то, что
появилось за сутки.
tasksN)hourstitler   
 )jsonloadsrM   	TypeError
isinstancedictr   listr   nowr   utcr   floatrK   r>   rP   r@   )
payloadsince_hoursr]   r6   rR   edgerB   rN   rT   stamps
             r   select_tasksrd   h   s   zz'" ",D$!7!7DHHWTEeT""	D/X\\(,,/YU;%788
C$%%'"(b11$<BBDt$E}

4  J/ 	" 	s   D, ,E Ec           	        [        5       n[        U R                  5      nU(       d  [        S5        gU(       d  [        S5        gU H  u  p4SR	                  [        5       U5      n [        XQ5      n[        X`R                  5       H  nUR                  S5      =(       d    SR                  SS	5      R!                  5       n	S
R	                  X95      n
U R"                  (       a#  U
SR	                  XHR                  S5      5      -  n
[%        U
5        M     M     g! [        R                  R                  [        [        4 a'  n[        SR	                  X4U5      5         S nAGM  S nAff = f)Nu=   нет TICKTICK_ACCESS_TOKEN — идеи не собираюr   uf   список TICKTICK_LISTS пуст или не разобрался — идеи не собираюz{}/project/{}/datau0   список «{}» ({}) не ответил: {}rT   r   rU   rV   z	- [{}] {}z (pid={} id={})id)r   rG   listsr   r   r   r3   r'   errorURLErrorOSErrorrM   rd   ra   r   rK   r>   with_markersr   )argsr0   rg   rD   rF   r/   r`   excrN   rT   lines              r   cmd_listro      s&   HE

#ELMuv	"))%'37	s*G !*:*:;DXXg&,"55dC@FFHE%%d2D   )00hhtnEE$K <    %%w
; 	CJJ4VYZ[	s   (D)E,E''E,z(pid=([A-Za-z0-9]+).*?\bid=([A-Za-z0-9]+)c                    / nU R                  5        HR  n[        R                  U5      nU(       d  M!  UR                  UR	                  S5      UR	                  S5      45        MT     U$ )u?  Пары (pid, id) из строк .taken-файла, по одной на строку.

`id=` ищется после `pid=` именно как отдельное слово (`\b`): иначе
регулярка нашла бы «id» внутри самого «pid» и закрыла бы не ту задачу.
      )
splitlines	MARKER_REsearchr@   group)textrB   rn   ms       r   parse_markersry      sW     C!T"1JJ
AGGAJ/0 " Jr   c           
        [        5       nU(       d  [        S5        g [        U R                  SS9 n[	        UR                  5       5      nS S S 5        W(       d%  [        SR                  U R                  5      5        gSnU H  u  pgSR                  [        5       Xg5      nS	n	[        S[        S-   5       Hn  n
 [        X5      n[        S
R                  XzU5      5        S[        U5      s=::  a  S:  a  O  OSn	  O+ U
[        :  d  MT  ["        R$                  " [&        5        Mp     U	(       a  M  US-  nM     U(       a%  [        SR                  U[)        U5      5      5        gg! , (       d  f       GN$= f! [         a/  n[        SR                  U R                  U5      5         S nAgS nAff = f! [        R                  R                  [        [         4 a&  n[        SR                  XzU5      5         S nAGNS nAff = f)Nu?   нет TICKTICK_ACCESS_TOKEN — идеи не закрываюrq   r&   )encodingu   не прочитал {}: {}u6   в {} нет строк с маркерами pid=/id=r   z{}/project/{}/task/{}/completeFu.   закрыла {} (попытка {}): HTTP {}   i,  Tu9   закрыть {} (попытка {}) не вышло: {}u.   не закрылось задач: {} из {})r   r   open	from_filery   r,   rj   r   r   rangeCOMPLETE_ATTEMPTSr9   intr'   rh   ri   rM   timesleepCOMPLETE_PAUSElen)rl   r0   fhpairsrm   failedrF   tidr/   okattemptcodes               r   cmd_completer      s   HENO$..73r!"''),E 4
 ELLT^^\]F.55egsHQ 1A 56G	l ,ELLS[_`a#d))c)B * **

>* 7 raKF# $ =DDVSQVZXY? 43 +224>>3GH$ LL))7J? l PWWX[fijkklsR   E< E*E< =>F8<F8*
E94E< 9E< <
F5%F00F58)H!HHc                    [         R                  " [        S9n U R                  SSS9nUR	                  SSS9nUR                  S[        R                  R                  S	S
5      S9  UR                  S[        S SS9  UR                  SSSS9  UR                  [        S9  UR	                  SSS9nUR                  SSS9  UR                  [        S9  U $ )N)descriptioncmdT)destrequiredr\   u+   собрать идеи из списков)helpz--listsTICKTICK_LISTSr   )defaultz--since-hoursuS   только идеи не старше N часов (без флага — все))typer   r   z--with-markers
store_trueuZ   дописывать (pid=… id=…) — нужно, чтобы потом закрыть)actionr   )funccompleteu$   закрыть взятые идеиz--from-file)r   )argparseArgumentParser__doc__add_subparsers
add_parseradd_argumentr   r   r   r_   set_defaultsro   r   )parsersubp_listp_dones       r   build_parserr      s    $$9F


UT

:C^^F)V^WF
	2::>>:JB+OP
eTr  t
(y  {
X&^^J-S^TF
5
\*Mr   c                V    [        5       R                  U 5      nUR                  U5      $ N)r   
parse_argsr   )argvrl   s     r   mainr      s"    >$$T*D99T?r   __main__)returnstr)r   r   r   None)r/   r   r0   r   r   r   )r/   r   r0   r   r   r   )rA   r   )rN   r[   )NN)r`   r   )r   r   )rw   r   )r   zargparse.ArgumentParserr   )$r   
__future__r   r   rW   r   rer   r   urllib.errorr'   urllib.requestr   r   r   r   r+   r   r   r   r   r   r3   r9   rG   rP   rd   ro   compilert   ry   r   r   r   __name__
SystemExitr   r   r   <module>r      s   0 #   	 	 
    2 21
 ?77+& F: JJBC	%P$
 z
TV
 r   