517 lines
17 KiB
Nim
517 lines
17 KiB
Nim
# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- #
|
|
# Bruno Charest
|
|
# 2022-08-09
|
|
#
|
|
# __ DESCRIPTIONS __
|
|
# joplin_utils : procedure related joplin application
|
|
#
|
|
# --==--==--==--==--==--==--==--==--==--==--==--==--==--==--==-- #
|
|
import os
|
|
import net
|
|
import times
|
|
import osproc
|
|
import jester
|
|
import std/json
|
|
import httpcore
|
|
import strutils
|
|
import strformat
|
|
import std/options
|
|
import std/httpclient
|
|
import std/asyncdispatch
|
|
|
|
import utils # Joplin utils procedures and types
|
|
|
|
# from os import sleep
|
|
from posix import read, write, fdatasync, close
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# TYPE : Setup joplin_ping data
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
type
|
|
joplin_ping* = object
|
|
ping_result*: seq[string]
|
|
ping_status*: bool
|
|
req*: Request
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# TYPE : Setup joplin_tags data
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
type
|
|
joplin_tags* = object
|
|
id*, parent_id*, title*: seq[string]
|
|
created_time*, updated_time*, user_created_time*, user_updated_time*,
|
|
is_shared*: seq[int]
|
|
req*: Request
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# TYPE : Setup joplin_notebooks data
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
type
|
|
joplin_notebooks* = object
|
|
id*, parent_id*, title*, share_id*, icon*: seq[string]
|
|
created_time*, updated_time*, user_created_time*, user_updated_time*,
|
|
is_shared*: seq[int]
|
|
req*: Request
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# TYPE : Setup joplin_notes data
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
type
|
|
joplin_notes* = object
|
|
id*, parent_id*, title*, body*, author*, source_url*, source*,
|
|
source_application*, application_data*, share_id*,
|
|
conflict_original_id*, body_html*, base_url*: seq[string]
|
|
updated_time*, is_conflict*, is_todo*, todo_due*,
|
|
todo_completed*, user_created_time*, user_updated_time*,
|
|
markup_language*, is_shared*: seq[int]
|
|
latitude*, longitude*, altitude*, order*: seq[float]
|
|
created_time*: seq[string]
|
|
req*: Request
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# TYPE : Setup joplin_note data
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
type
|
|
joplin_note* = object
|
|
id*, parent_id*, title*, body*, author*, source_url*, source*,
|
|
source_application*, application_data*, conflict_original_id*,
|
|
master_key_id*, share_id*, encryption_cipher_text * : string
|
|
created_time*, updated_time*, user_created_time*, user_updated_time*,
|
|
is_conflict*, is_todo*, todo_due*, todo_completed*, markup_language*,
|
|
is_shared*, encryption_applied*: int
|
|
latitude*, longitude*, altitude*, order*: float
|
|
req*: Request
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# TYPE : Setup process data structure
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
type
|
|
program* = object
|
|
pid*: int
|
|
stdout*: string
|
|
stderr*: string
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# DURATION : duration variables
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
let
|
|
resetDuration = initDuration(seconds = 2)
|
|
deciSecondDuration* = initDuration(milliseconds = 100)
|
|
qtrsecondDuration* = initDuration(milliseconds = 250)
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# VAR : connexion http variables
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
var
|
|
client = newHttpClient()
|
|
lastConnection = getTime().utc
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : reset HTTP client
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc resetHttpClient() =
|
|
if (getTime().utc - lastConnection) > resetDuration:
|
|
# Probably a new timeout. We have not yet experienced a long outage.
|
|
# We may however be entering an extended outage.
|
|
# Creating the new clients seems to use up lots of CPU.
|
|
# I want to do that as little as possible.
|
|
try:
|
|
client.close()
|
|
except:
|
|
echo("Attempted to close clients. Probably do not exist.")
|
|
echo("Current exception: ", getCurrentExceptionMsg())
|
|
client = newHttpClient(timeout = 500)
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==--==--==--==-==--==--==- #
|
|
# PROC : use the joplin API ping to validate service availibility
|
|
# --==--==--==--==--==--==--==--==--==--==--==--==--==-==--==--==- #
|
|
proc ping_joplin*(token: string): Future[joplin_ping] {.async.} =
|
|
|
|
var j_p: joplin_ping
|
|
var client = newAsyncHttpClient()
|
|
var url: string
|
|
j_p.ping_status = false
|
|
try:
|
|
url = fmt"http://localhost:41184/ping?token={token}"
|
|
var json = await client.getContent(url)
|
|
j_p.ping_result.add(json)
|
|
|
|
echo j_p.ping_result[0]
|
|
|
|
if j_p.ping_result[0] == "JoplinClipperServer":
|
|
j_p.ping_status = true
|
|
|
|
except TimeoutError, IOError, OSError:
|
|
# I naively think I would see this thrown or the plain except below.
|
|
# But I almost never see an Error raised.
|
|
echo("Current Exception: ", getCurrentException().name)
|
|
echo("Current Exception Msg: ", getCurrentExceptionMsg())
|
|
echo("Sleeping for 1 seconds at: ", getTime().utc)
|
|
sleep(500)
|
|
resetHttpClient()
|
|
j_p.ping_status = false
|
|
except:
|
|
echo("Current Exception: ", getCurrentException().name)
|
|
echo("Current Exception Msg: ", getCurrentExceptionMsg())
|
|
echo("Sleeping for 1 seconds at: ", getTime().utc)
|
|
j_p.ping_status = false
|
|
return j_p
|
|
# parse jason
|
|
#let joplin_notes_Json = parseJson(json)
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : get all joplin NOTEBOOKS
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc get_joplin_notebooks*(token: string): Future[joplin_notebooks] {.async.} =
|
|
|
|
# Variables
|
|
var j_nb: joplin_notebooks
|
|
var has_more: bool = true
|
|
var page: int = 1
|
|
var url: string
|
|
var client = newAsyncHttpClient()
|
|
var pingCheck: joplin_ping
|
|
|
|
pingCheck = waitFor ping_joplin(token)
|
|
|
|
# check the joplin status befor query
|
|
if pingCheck.ping_status:
|
|
|
|
# make sure to check all pages
|
|
while has_more == true:
|
|
|
|
# request joplin API for notebooks
|
|
url = fmt"http://localhost:41184/folders?page={page}&token={token}"
|
|
echo("URL notebooks : ", url)
|
|
var json = await client.getContent(url)
|
|
|
|
# parse jason
|
|
let joplin_notebooks_Json = parseJson(json)
|
|
|
|
# valider qu'il n'y a plus de page
|
|
if not joplin_notebooks_Json["has_more"].getBool:
|
|
has_more = false
|
|
|
|
# store json info into an object
|
|
var count: int = 1
|
|
for nb in joplin_notebooks_Json["items"]:
|
|
j_nb.id.add(nb["id"].getstr)
|
|
j_nb.parent_id.add(nb["parent_id"].getstr)
|
|
j_nb.title.add(nb["title"].getstr)
|
|
count += 1
|
|
|
|
# aller à la page suivante
|
|
page += 1
|
|
|
|
return j_nb
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : get all joplin NOTES
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc get_joplin_notes*(token: string): Future[joplin_notes] {.async.} =
|
|
|
|
# Variables
|
|
var j_notes: joplin_notes
|
|
var has_more: bool = true
|
|
var page: int = 1
|
|
var url: string
|
|
var client = newAsyncHttpClient()
|
|
var pingCheck: joplin_ping
|
|
pingCheck = waitFor ping_joplin(token)
|
|
|
|
# check the joplin status befor query
|
|
if pingCheck.ping_status:
|
|
|
|
# make sure to check all pages
|
|
while has_more == true:
|
|
|
|
# request joplin API for Notes
|
|
url = fmt"http://localhost:41184/notes?page={page}&fields=id,parent_id,title,created_time,updated_time&token={token}"
|
|
echo("URL notes : ", url)
|
|
var json = await client.getContent(url)
|
|
|
|
# parse jason
|
|
let joplin_notes_Json = parseJson(json)
|
|
|
|
# valider qu'il n'y a plus de page
|
|
if not joplin_notes_Json["has_more"].getBool:
|
|
has_more = false
|
|
|
|
#echo joplin_notes_Json
|
|
|
|
# store json info into an object
|
|
var count: int = 1
|
|
var epochTime: int
|
|
var humanTime: Time
|
|
for nb in joplin_notes_Json["items"]:
|
|
j_notes.id.add(nb["id"].getstr)
|
|
j_notes.parent_id.add(nb["parent_id"].getstr)
|
|
j_notes.title.add(nb["title"].getstr)
|
|
#epochTime = nb["created_time"].getInt
|
|
#humanTime = fromUnix(convert(Milliseconds, Seconds, epochTime))
|
|
|
|
j_notes.created_time.add(convertEpochToHumanTime(nb[
|
|
"created_time"].getInt))
|
|
count += 1
|
|
|
|
# aller à la page suivante
|
|
page += 1
|
|
|
|
return j_notes
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : get joplin NOTE
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc get_joplin_note*(token: string, noteid: string): Future[
|
|
joplin_note] {.async.} =
|
|
|
|
# Variables
|
|
var j_note: joplin_note
|
|
var has_more: bool = true
|
|
var page: int = 1
|
|
var url: string
|
|
var client = newAsyncHttpClient()
|
|
|
|
# Check joplin connection availibility
|
|
var pingCheck: joplin_ping
|
|
pingCheck = waitFor ping_joplin(token)
|
|
|
|
|
|
# Query for a selected note
|
|
url = fmt"http://localhost:41184/notes/{noteid}?fields=id,parent_id,title,body,created_time,updated_time,is_conflict,latitude,longitude,altitude,author,source_url,is_todo,todo_due,todo_completed,source,source_application,application_data,order,user_created_time,user_updated_time,encryption_cipher_text,encryption_applied,markup_language,is_shared,share_id,conflict_original_id,master_key_id&token={token}"
|
|
echo("URL notes : ", url)
|
|
var jsonNote = await client.getContent(url)
|
|
|
|
# parse jason
|
|
let joplin_note_Json = parseJson(jsonNote)
|
|
|
|
# get all informations for a selected note
|
|
j_note.id = joplin_note_Json["id"].getstr
|
|
j_note.parent_id = joplin_note_Json["parent_id"].getstr
|
|
j_note.title = joplin_note_Json["title"].getstr
|
|
j_note.body = joplin_note_Json["body"].getstr
|
|
j_note.author = joplin_note_Json["author"].getstr
|
|
j_note.source_url = joplin_note_Json["source_url"].getstr
|
|
j_note.source = joplin_note_Json["source"].getstr
|
|
j_note.source_application = joplin_note_Json["source_application"].getstr
|
|
j_note.application_data = joplin_note_Json["application_data"].getstr
|
|
j_note.share_id = joplin_note_Json["share_id"].getstr
|
|
j_note.conflict_original_id = joplin_note_Json["conflict_original_id"].getstr
|
|
j_note.created_time = joplin_note_Json["created_time"].getInt
|
|
j_note.updated_time = joplin_note_Json["updated_time"].getInt
|
|
j_note.is_conflict = joplin_note_Json["is_conflict"].getInt
|
|
j_note.latitude = joplin_note_Json["latitude"].getFloat
|
|
j_note.longitude = joplin_note_Json["longitude"].getFloat
|
|
j_note.altitude = joplin_note_Json["altitude"].getFloat
|
|
j_note.is_todo = joplin_note_Json["is_todo"].getInt
|
|
j_note.todo_due = joplin_note_Json["todo_due"].getInt
|
|
j_note.todo_completed = joplin_note_Json["todo_completed"].getInt
|
|
j_note.order = joplin_note_Json["order"].getFloat
|
|
j_note.user_created_time = joplin_note_Json["user_created_time"].getInt
|
|
j_note.user_updated_time = joplin_note_Json["user_updated_time"].getInt
|
|
j_note.encryption_cipher_text = joplin_note_Json[
|
|
"encryption_cipher_text"].getstr
|
|
j_note.encryption_applied = joplin_note_Json["encryption_applied"].getInt
|
|
j_note.markup_language = joplin_note_Json["markup_language"].getInt
|
|
j_note.is_shared = joplin_note_Json["is_shared"].getInt
|
|
j_note.master_key_id = joplin_note_Json["master_key_id"].getstr
|
|
|
|
|
|
echo j_note
|
|
return j_note
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : get all joplin TAGS
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc get_joplin_tags*(token: string): Future[joplin_tags] {.async.} =
|
|
|
|
# Variables
|
|
var j_tags: joplin_tags
|
|
var has_more: bool = true
|
|
var page: int = 1
|
|
var url: string
|
|
var client = newAsyncHttpClient()
|
|
var pingCheck: joplin_ping
|
|
pingCheck = waitFor ping_joplin(token)
|
|
|
|
# check the joplin status befor query
|
|
if pingCheck.ping_status:
|
|
|
|
# make sure to check all pages
|
|
while has_more == true:
|
|
|
|
# request joplin API for notebooks
|
|
url = fmt"http://localhost:41184/tags?page={page}&token={token}"
|
|
echo("URL tags : ", url)
|
|
var json = await client.getContent(url)
|
|
|
|
# parse jason
|
|
let joplin_tags_Json = parseJson(json)
|
|
|
|
# valider qu'il n'y a plus de page
|
|
if not joplin_tags_Json["has_more"].getBool:
|
|
has_more = false
|
|
|
|
# store json info into an object
|
|
var count: int = 1
|
|
for nb in joplin_tags_Json["items"]:
|
|
j_tags.id.add(nb["id"].getstr)
|
|
j_tags.parent_id.add(nb["parent_id"].getstr)
|
|
j_tags.title.add(nb["title"].getstr)
|
|
count += 1
|
|
|
|
# aller à la page suivante
|
|
page += 1
|
|
|
|
return j_tags
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : get the token from Joplin Terminal
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc get_joplin_cli_token*(): string =
|
|
var flagName: string = ""
|
|
var flagValue: string = ""
|
|
var result = execCmdEx("joplin config api.token")
|
|
|
|
if result.exitCode == 0:
|
|
let param1 = result.output
|
|
let flagSplit = param1.split(" = ")
|
|
flagName = flagSplit[0]
|
|
flagValue = flagSplit[1]
|
|
|
|
return flagValue
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : launch any program and get PID
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc launchProgram(app: string = "", workingPath: string = "", arguments: array[
|
|
2, string]): int {.thread.} =
|
|
|
|
var p = startProcess(joinPath(workingPath, app), workingPath, arguments)
|
|
let pid = p.processID()
|
|
var outhdl = outputHandle(p)
|
|
var inputhdl = inputHandle(p)
|
|
var errhdl = errorHandle(p)
|
|
return pid
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : Start joplin cli
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc joplin_cli_start*(): int {.thread.} =
|
|
|
|
let joplinProcessId = launchProgram("joplin", "/usr/bin/", ["server", "start"])
|
|
return joplinProcessId
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : stop Joplin Terminal
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc joplin_cli_stop*(): int {.thread.} =
|
|
let joplinProcessId = launchProgram("joplin", "/usr/bin/", ["server", "stop"])
|
|
return joplinProcessId
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : check Joplin Terminal staus
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc joplin_cli_status*(): bool =
|
|
var rc = false
|
|
var result = execCmdEx("joplin server status")
|
|
|
|
if result.exitCode == 0:
|
|
if "Server is not running" in result.output:
|
|
echo "Joplin Terminal cli status is down : ", result.output
|
|
rc = false
|
|
else:
|
|
echo "Joplin Terminal cli status is up : ", result.output
|
|
rc = true
|
|
else:
|
|
echo "Error validate joplin terminal status : ", result.output
|
|
return rc
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC : start or stop Joplin Terminal
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
proc joplin_cli_start_stop*(): int =
|
|
var isStart: int = 0
|
|
var sleep_delay_frame: int = 50
|
|
var sleep_max: int = 5000
|
|
|
|
if joplin_cli_status() == false:
|
|
isStart = joplin_cli_start()
|
|
while joplin_cli_status() == false:
|
|
sleep(sleep_delay_frame)
|
|
echo "Joplin client Terminal started: ", isStart
|
|
|
|
else:
|
|
echo "Joplin client Terminal is alredy started !"
|
|
#if joplin_cli_status() == true:
|
|
isStart = joplin_cli_stop()
|
|
while joplin_cli_status() == true:
|
|
sleep(sleep_delay_frame)
|
|
echo "Joplin client Terminal stopped: ", isStart
|
|
|
|
return isStart
|
|
|
|
|
|
|
|
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
# PROC :
|
|
# --==--==--==--==--==--==--==--==--==--==-- #
|
|
|
|
# proc get_joplin_tags_json*(token:string): Future[] {.async.} =
|
|
|
|
# # url = fmt"http://localhost:41184/notes/{id}?fields=id,parent_id,title,body,created_time,updated_time,is_conflict,latitude,longitude,altitude,author,source_url,is_todo,todo_due,todo_completed,source,source_application,application_data,order,user_created_time,user_updated_time,encryption_cipher_text,encryption_applied,markup_language,is_shared,share_id,conflict_original_id,master_key_id&token={token}"
|
|
|
|
# # url = fmt"http://localhost:41184/tags/{id}?fields=id,parent_id,title,created_time,updated_time,user_created_time,user_updated_time,is_shared&token={token}"
|
|
|
|
# # http://localhost:41184/tags?token=e5f6644fbf6a97ddc55648dae72b11caecda6c6642d8ce0d3b20129b89b196385737eb908923542c3343649ebbf865b55bda031ab4c3a16edc7723ef2ad77d8f
|
|
|
|
# # Variables
|
|
# var j_tags: joplin_tags
|
|
# var has_more: bool = true
|
|
# var page: int = 1
|
|
# var url: string
|
|
# var client = newAsyncHttpClient()
|
|
# var pingCheck: joplin_ping
|
|
# pingCheck = waitFor ping_joplin(token)
|
|
|
|
|
|
# # check the joplin status befor query
|
|
# if pingCheck.ping_status:
|
|
|
|
# # make sure to check all pages
|
|
# while has_more == true:
|
|
|
|
# # request joplin API for notebooks
|
|
# url = fmt"http://localhost:41184/tags?page={page}&token={token}"
|
|
# echo("URL tags : ", url)
|
|
# var json = await client.getContent(url)
|
|
|
|
# # parse jason
|
|
# let joplin_tags_Json = parseJson(json)
|
|
|
|
# # valider qu'il n'y a plus de page
|
|
# if not joplin_tags_Json["has_more"].getBool:
|
|
# has_more = false
|
|
|
|
# # store json info into an object
|
|
# var count: int = 1
|
|
# for nb in joplin_tags_Json["items"]:
|
|
# echo nb["id"].getstr
|
|
# echo nb["parent_id"].getstr
|
|
# echo nb["title"].getstr
|
|
# count += 1
|
|
|
|
# # aller à la page suivante
|
|
# page += 1
|
|
|
|
|